Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Изменение DOM с помощью innerHTML (https://javascript.ru/forum/misc/64792-izmenenie-dom-s-pomoshhyu-innerhtml.html)

moron 05.09.2016 18:49

Изменение DOM с помощью innerHTML
 
Здраствуйте.
Получилось, что из-за собственных пробелах в знаниях я долго бился с неочевидным(возможно лишь для меня) поведением.
Опытным путем мне удалось найти источник проблемы. Проблема была в добавлении элементов дом с помощью innerHTML: после добавление таким способом к полученным ранее элементам с помощью jQuery не работали методы insertAfter или просто after(возможно не работало вообще все. Вплоть до изменение стилей).
Я сделал небольшое демо и 2 вариации.
Есть контенер с двумя типами элеметов(.b и .a). В .a примешиваются элементы .b с некоторым шансом.
Перед самим примешиванием я добавляю еще один .b элемент(разными спобами) и в варианте с innerHTML потом не могу работать с ранее полученными элементами. Если вставка через append(понятно, что метод append jquery вызывает нативный Node.appendChild), то он не генерит новый дом, а лишь действительно вставляет новый элемент[исходник jq](поэтому старые элементы все еще куда-то указывают), в то время как elem.innerHTMl += "html_text" разворачивается в elem.innerHTML = elem.innerHTML + "html_text" и весь дом у нас получается новый, плюс происходит, что старые ноды никуда не деваются, ведь на них все еще указывается jquery like обьект(и потенциально мы выхватываем лишнию память, для уже несуществующих узлов - да?)...
Итак, демо:
1) Вариант с innerHTML
2) Вариант с append
Какая помощь мне нужна:
Какой литературой я могу восполнить свои знания и в каких еще случаях я могу столкнуться с такими проблемами?
Есть ли в моих размышлениях какие-то ошибки?
Я наслышан, что прямые методы работы с dom занимают больше времени, чем простой innerHTML, но что если у нас вставки в dom происходят достаточно часто и относительно большими пачками(штук 20), при этом к тому моменту уже могут находиться ~100элементов и перерисовывать их нежелательно из-за усложенения анимации :с Может ли быть такое, что использовать innerHTML на каком-то этапе станет менее оптимально, нежели вставка через dom методы?

laimas 05.09.2016 19:14

var $a = $(".a"),
    $b = $("#root").append("<div class='b'>b-dynamic</div>").find('.b');

//и 

var $a = $(".a");
$("#root").get(0).innerHTML += "<div class='b'>b-dynamic</div>";
var $b = $("div.b");


Иначе в цикле на добавленные не сослаться.

moron 05.09.2016 19:18

Суть не совсем в этом.
1) мы получаем $a
2) добавляем в $root.get(0).innerHTML += "строка с большим количеством .b"
3) теперь $a так же недоступен.
Т.е второй вариант, тобой предложенный так же не сможет работать с .a после innerHTML

var $a = $(".a");
$("#root").get(0).innerHTML += "<div class='b'>b-dynamic</div>";
var $b = $("div.b");

moron 05.09.2016 19:21

Баг как раз был в том что после innerHTML переставали работать методы к $a, потому что он указывал на уже удаленные узлы. А удаленны они были, потому что $root.get(0).innerHTML += "html"(развернувшись в $root.get(0).innerHTML = $root.get(0).innerHTML + "html") взял и перезаписал ВСЁ содержимое, что было. В том числе и $a, которые были получены раньше.

moron 05.09.2016 19:25

Вот небольшой пример. Посмотри на комментарий в js
http://codepen.io/mogafk/pen/qaEVVV
т.е пример
var $a = $(".a");
$("#root").get(0).innerHTML += "<div class='b'>b-dynamic</div>";
var $b = $("div.b");

Так же имеет баг, потому что $a будет указывать на уже удаленные узлы..

laimas 05.09.2016 19:28

<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<style>

</style> 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script> 
$(function() {
var $a = $(".a"),
    $b = $("#root").append("<div class='b'>b-dynamic</div>").find('.b');
console.log($a.length, $b.length)

$("#root").get(0).innerHTML += "<div class='b'>b-dynamic</div>";
var $b = $("div.b");

console.log($a.length, $b.length)
});

</script>     
</head> 

<body>
<div id="root">
<div class="a">a</div>
<div class="a">a</div>
<div class="a">a</div>
  <hr />
<div class="b">b</div>
<div class="b">b</div>
<div class="b">b</div>
</div>
</body> 
</html>


Хоть как добавлять, доступными они будут. А вот то что вы получили коллекцию, с которой хотите работать, а затем модифицировали DOM, вот тут могут быть неожиданности.

moron 05.09.2016 19:32

Длина будет правильной и даже содержимое коллекций будет верным, но на самом деле это будут лишь уже несуществующие дубликаты из dom, которые находятся лишь в памяти. Т.е любая манипуляция с $a не будет работать как ожидается(возможно изменения будут применяться к элементам, которых в dom в действительно уже НЕТ).
Просто модицифкация дом не даст такого поведения. Потому что не удалит все и не запишет заново, а лишь сделает частичные изменеия :с

moron 05.09.2016 19:33

т.е ты прав, что они будут доступным, но какой в этом смысл, если их нет в дом на самом деле?

laimas 05.09.2016 19:35

Цитата:

Сообщение от moron
но какой в этом смысл, если их нет в дом на самом деле?

Что значит нет? Консоль по примеру покажет
3, 4
3, 5

пятый что "бог подарил" или все такие есть в DOM?

PS. Видимо вопрос касается некоторых тонкостей присущих innerHTML, и речь об этом. Тогда здесь почитать.

moron 05.09.2016 19:38

.b пятый есть и .b четвертый, а вот a. нет в дом вообще после innerHTML.
Иначе как ты обьяснишь, что после innerHTML пропадает родитель у .a?
console.log($a.eq(0).parent().prop("tagName"))

moron 05.09.2016 19:40

Если они действительно есть в dom, то как ты обьяснишь, что у $a после innerHTML пропадает родитель?
console.log($a.eq(0).parent().prop("tagName"))

laimas 05.09.2016 19:40

По ссылке, видимо вопрос этого касается или я не понимаю о чем спрашивают.

laimas 05.09.2016 19:41

Цитата:

Сообщение от moron
что у $a после innerHTML пропадает родитель?

Никуда он не пропадет - отладчик откройте и ...

moron 05.09.2016 19:52

Я открыл и получаю undefined (google chrome 52.0.2743.82 (64-bit))
И это правильное поведение. По ссылке, которую ты мне дал
"Добавление innerHTML+= осуществляет перезапись"
и именно потому что перезапись, потому что на самом деле элементы из dom удаляются, но в коллекции на них остается ссылка, поэтому они остаются в памяти. И именно поэтому у него меняется родитель..
Либо ты не понимаешь этого, либо ты не понимаешь, что я хочу.
В любом случае спасибо :с

* Сообщения дублировались, потому что я не заметил, как мы перешли на вторую страницу.

laimas 05.09.2016 19:53

Цитата:

Сообщение от moron
Иначе как ты обьяснишь, что после innerHTML у .a пропадает родитель?

Еще раз - никуда он не пропадает, а содержимое перезаписывается. Вы по ссылке прочли?

Вы получили коллекцию, затем модифицировали DOM и хотите чтобы при этом старые ссылки работали как надо?

$("#root").get(0).innerHTML += "<div class='b'>b-dynamic</div>";
var $b = $("div.b"),
    $a = $(".a")
console.log($a.eq(0).parent().prop("tagName"))


Все на месте?

moron 05.09.2016 19:57

В этом случае да. В моем случае добавление перед получением коллекций сложноосуществимо...
Поэтому я отказался в конкретном случае от innerHTML.
За ссылку спасибо :с За внимание тоже.
Цитата:

модифицировали DOM и хотите чтобы при этом старые ссылки работали как надо
Да. Неочивидность как раз том, что innerHTML ничего не модифицирует, а удаляет все и пишет новое..

Про исчезновение родителя я говорил в таком коде:
$a = $(".a")
$root[0].innerHTML += "1";
console.log($a.eq(0).parent().prop("tagName"))

laimas 05.09.2016 20:13

Цитата:

Сообщение от moron
innerHTML ничего не модифицирует, а удаляет все и пишет новое..

Ну здрастье, то есть перезапись это не модификация? :)
Просто достаточно знать это. Например, можно root делегировать обработчик щелчков по дочерним DIV. Затем добавлять в него новые дочерние DIV, как при этом они будут добавляться не важно, все будут работать и будет доступно. Но то что вы пытаетесь делать уже не получится.

moron 05.09.2016 21:26

С делегирование все дочтаточно очевидно, потому что мы вешаем обработчик на родителя а внутри обработчика проверяем event.target. Нам все равно, есть ли обьекты, когда они были добавлены и т.д

laimas 06.09.2016 01:44

Цитата:

Сообщение от moron
С делегирование все дочтаточно очевидно, потому что мы вешаем обработчик на родителя

Ну так и обратное очевидно - получили ссылку на объект, перезаписали его, ссылка не действительна. Но ничто не запрещает сначала получить ссылку на родителя через дочерний элемент, а только затем третировать DOM.

Vlasenko Fedor 06.09.2016 01:56

insertAdjacentHTML не пробовали :cray:

warren buffet 06.09.2016 05:34

Цитата:

Сообщение от moron
Я наслышан, что прямые методы работы с dom занимают больше времени, чем простой innerHTML

То есть имея нулевые знания и опыт программирования, ты уже наслышан о какой-то химере и пытаешься что-то оптимизировать. Так ты ничему не научишься, поскольку у тебя мотивация анальная (это по Фрейду, а не по Павлову).

moron 06.09.2016 20:28

Едва ли можно назвать химерой старичка innerHTML. Мне казалось логичным приписывать интерполированную мусташем html строку именно с помощью innerHTML. Чем делать цикл в котором создавать элементы с элементами(причем шаблоны так же отличаются. Делать это без какого-либо шаблонизатора мракобесие) подставляя значения из своеобразной модели. В любом случае я пытался избежать тесной работы dom. Про insertAdjacentHTML я не знал(а может забыл).
Создав тему я наивно надеялся, что не встречу любителей Фрейда или своебразной мотивации, а лишь получу какие-то весомые замечания и знания связанные именно с программированием. Хотя на самом деле стоило лишь самостоятельно погуглить.

warren buffet 07.09.2016 10:11

Я вообще не вникал в твой бред. Если хочешь вникать глубже, гугли Shadow Dom.

moron 07.09.2016 19:55



Часовой пояс GMT +3, время: 15:44.