Подтвердить что ты не робот

Почему должен быть y.innerHTML = x.innerHTML; избегать?

Скажем, что у нас есть DIV x на странице, и мы хотим дублировать ( "copy-paste" ) содержимое этого DIV в другой DIV y. Мы могли бы сделать это так:

y.innerHTML = x.innerHTML;

или с помощью jQuery:

$(y).html( $(x).html() );

Однако, похоже, этот метод не является хорошей идеей и что его следует избегать.

(1) Почему следует избегать этого метода?

(2) Как это сделать вместо этого?


Update:
Ради этого вопроса допустим, что в DIV x нет элементов с идентификатором. (Извините, что я забыл рассмотреть этот случай в моем первоначальном вопросе.)

Вывод:
Я опубликовал свой собственный ответ на этот вопрос ниже (как я и предполагал). Теперь я также планировал принять свой собственный ответ :P, но ответ на lonesomeday настолько изумительный, что я должен принять его вместо этого.

4b9b3361

Ответ 1

Этот метод "копирования" HTML-элементов из одного места в другое является результатом неправильного понимания того, что делает браузер. Браузеры не хранят HTML-документ в памяти где-то и многократно изменяют HTML на основе команд из JavaScript.

Когда браузер сначала загружает страницу, он анализирует документ HTML и превращает его в структуру DOM. Это отношение объектов, следующих по стандарту W3C (ну, в основном...). С тех пор исходный HTML полностью избыточен. В браузере все равно, какова была исходная структура HTML; его понимание веб-страницы - это структура DOM, которая была создана из нее. Если ваша разметка HTML неверна/недействительна, она будет каким-то образом исправлена ​​веб-браузером; структура DOM никак не будет содержать недопустимый код.

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

Поэтому он не должен использоваться для изменения существующей веб-страницы. DOM (Document Object Model) имеет систему для изменения содержимого страницы. Это основано на взаимосвязи узлов, а не на сериализации HTML. Поэтому, когда вы добавляете li в ul, у вас есть эти две опции (предполагая, что ul - элемент списка):

// option 1: innerHTML
ul.innerHTML += '<li>foobar</li>';

// option 2: DOM manipulation
var li = document.createElement('li');
li.appendChild(document.createTextNode('foobar'));
ul.appendChild(li);

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

Второе важное соображение - подумать об ограничениях HTML. Когда вы думаете о веб-странице, не все, что имеет отношение к элементу, может быть сериализовано в HTML. Например, обработчики событий, связанные с x.onclick = function(); или x.addEventListener(...), не будут реплицироваться в innerHTML, поэтому они не будут скопированы. Таким образом, новые элементы в y не будут иметь прослушиватели событий. Вероятно, это не то, что вы хотите.

Итак, путь вокруг этого заключается в том, чтобы работать с собственными методами DOM:

for (var i = 0; i < x.childNodes.length; i++) {
    y.appendChild(x.childNodes[i].cloneNode(true));
}

Чтение документации MDN, вероятно, поможет понять этот способ:

Теперь проблема с этим (как и с вариантом 2 в примере выше) заключается в том, что он очень многословный, гораздо длиннее, чем опция innerHTML. Это когда вы цените наличие библиотеки JavaScript, которая делает это для вас. Например, в jQuery:

$('#y').html($('#x').clone(true, true).contents());

Это гораздо более явное описание того, что вы хотите. Например, с различными преимуществами производительности и сохранением обработчиков событий, это также помогает понять, что делает ваш код. Это хорошо для вашей души как программиста JavaScript и делает причудливые ошибки значительно менее вероятными!

Ответ 2

  • Вы можете дублировать идентификаторы, которые должны быть уникальными.
  • jQuery метод clone вызывает, $(element).clone(true); будет клонировать данные и прослушиватели событий, но ID все равно будет быть клонированным. Поэтому, чтобы избежать дублирования идентификаторов, не используйте идентификаторы для объектов, которые необходимо клонировать.

Ответ 3

  • Следует избегать, потому что тогда вы теряете любые обработчики, которые могли быть на этом DOM.

  • Вы можете попытаться обойти это, добавив клоны элементов DOM, а не полностью перезаписывая их.

Ответ 4

Сначала определим задачу, которая должна быть выполнена здесь:

Все дочерние узлы DIV x должны быть "скопированы" (вместе со всеми его потомками = глубокая копия) и "вставлены" в DIV y. Если у какого-либо из потомков x есть один или несколько обработчиков событий, связанных с ним, мы предположительно хотим, чтобы эти обработчики продолжали работать над копиями (после того, как они были помещены внутри y).

Теперь это не тривиальная задача. К счастью, библиотека jQuery (и все другие популярные библиотеки, как я полагаю) предлагает удобный метод для выполнения этой задачи: .clone(). Используя этот метод, решение может быть написано так:

$( x ).contents().clone( true ).appendTo( y );

Вышеупомянутое решение является ответом на вопрос (2). Теперь давайте рассмотрим вопрос (1):

Это

y.innerHTML = x.innerHTML;

- это не просто плохая идея - она ​​ужасная. Позвольте мне объяснить...

Вышеуказанное утверждение можно разбить на два этапа.

  • Выражается выражение x.innerHTML,

  • Возвращаемое значение этого выражения (которое является строкой) присваивается y.innerHTML.

Узлы, которые мы хотим скопировать (дочерние узлы x), являются узлами DOM. Это объекты, которые существуют в памяти браузера. При оценке x.innerHTML браузер сериализует (строит) эти узлы DOM в строку (строку исходного кода HTML).

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

На шаге 2 мы присваиваем эту строку y.innerHTML. Браузер оценивает это с помощью синтаксического анализа строки, которая приводит к набору узлов DOM, которые затем вставляются в DIV y (как дочерние узлы).

Итак, подведем итог:

дочерние узлы x строка → строка исходного кода HTML → синтаксический анализ → узлы (копии)

Итак, какая проблема с этим подходом? Ну, узлы DOM могут содержать свойства и функциональные возможности, которые не могут и поэтому не будут сериализованы. Самыми важными такими функциональными возможностями являются обработчики событий, связанные с потомками x - копии этих элементов не будут связаны с обработчиками событий. Обработчики потерялись в процессе.

Интересную аналогию можно сделать здесь:

Цифровой сигнал → Преобразование D/A → Аналоговый сигнал → Преобразование A/D → Цифровой сигнал

Как вы, наверное, знаете, результирующий цифровой сигнал не является точной копией исходного цифрового сигнала - некоторая информация потерялась в процессе.

Надеюсь, теперь вы поймете, почему следует избегать y.innerHTML = x.innerHTML.

Ответ 5

Я бы не сделал этого просто потому, что вы просите браузер повторно разгладить разметку HTML, которая уже была проанализирована.

Я бы более склонен использовать родной cloneNode(true) для дублирования существующих элементов DOM.

var node, i=0;

while( node = x.childNodes[ i++ ] ) {
    y.appendChild( node.cloneNode( true ) );
}

Ответ 6

Ну, это действительно зависит. Существует возможность создания повторяющихся элементов с одинаковым идентификатором, что никогда не бывает хорошим.

jQuery также имеет методы, которые могут сделать это для вас.