Почему "element.innerHTML + =" плохой код?
Мне сказали не добавлять файлы с помощью element.innerHTML += ...
следующим образом:
var str = "<div>hello world</div>";
var elm = document.getElementById("targetID");
elm.innerHTML += str; //not a good idea?
Что не так с этим?, какие у меня другие альтернативы?
Ответы
Ответ 1
Каждый раз, когда устанавливается innerHTML
, HTML должен быть проанализирован, DOM создан и вставлен в документ. Это требует времени.
Например, если elm.innerHTML
имеет тысячи div, таблиц, списков, изображений и т.д., то вызов .innerHTML += ...
приведет к тому, что синтаксический анализатор снова проанализирует все эти вещи. Это также может сломать ссылки на уже построенные элементы DOM и вызвать другой хаос. В действительности, все, что вы хотите сделать, это добавить один новый элемент в конец.
Лучше просто вызвать appendChild
:
var newElement = document.createElement('div');
newElement.innerHTML = '<div>Hello World!</div>';
elm.appendChild(newElement);
Таким образом, существующее содержимое elm
снова не обрабатывается.
ПРИМЕЧАНИЕ.. Возможно, [некоторые] браузеры достаточно умны, чтобы оптимизировать оператор +=
, а не повторно анализировать существующее содержимое. Я не исследовал это.
Ответ 2
Да, elm.innerHTML += str;
это очень плохая идея.
Типичный ответ "браузер должен перестроить DOM" действительно не оправдывает себя:
-
Сначала браузер должен просмотреть все элементы в elm, каждое из их свойств, все их тексты, комментарии и узлы процесса и экранировать их, чтобы создать строку.
-
Тогда у вас есть длинная строка, к которой вы добавляете. Этот шаг в порядке.
-
В-третьих, когда вы устанавливаете innerHTML, браузер должен удалить все элементы, свойства и узлы, которые он только что прошел.
-
Затем он анализирует строку, строит из всех уничтоженных элементов, свойств и узлов, чтобы создать новый фрагмент DOM, который в основном идентичен.
-
Наконец, он присоединяет новые узлы, и браузер должен создать макет целиком. Этого можно избежать (см. Альтернативу ниже), но даже если для добавленных узлов требуется компоновка, старые узлы будут кэшировать свои свойства компоновки, а не пересчитывать их заново.
-
Но это еще не сделано! Браузер также должен перезапускать старые узлы путем сканирования всех переменных JavaScript.
Проблемы:
-
Некоторые свойства могут не отражаться HTML, например, текущее значение <input>
будет потеряно и сброшено к исходному значению в HTML.
-
Если у вас есть какие-либо обработчики событий на старых узлах, они будут уничтожены, и вам придется заново присоединить их все.
-
Если ваш js-код ссылается на какие-либо старые узлы, они не будут уничтожены, а вместо этого будут потеряны. Они принадлежат документу, но больше не находятся в дереве DOM. Когда ваш код обращается к ним, ничего не может произойти, или это может вызвать ошибку.
-
Обе проблемы означают, что это плохо работает с плагинами js - плагины могут присоединять обработчики или запоминать старые узлы и вызывать утечку памяти.
-
Если вы привыкли выполнять манипуляции с DOM с помощью innerHTML, вы можете случайно изменить свойства или заняться другими вещами, которые вам не нужны.
-
Чем больше у вас узлов, тем больше это неэффективно, тем больше батарейного сока даром.
Короче говоря, это неэффективно, это подвержено ошибкам, это просто ленивый и неосведомленный.
Лучшая альтернатива - Element.insertAdjacentHTML
, которую я не видел в других ответах:
elm.insertAdjacentHTML( 'beforeend', str )
Почти такой же код, без проблем с внутренним HTML. Без перестройки, без потери обработчика, без сброса ввода, меньше фрагментации памяти, без вредных привычек, без ручного создания и назначения элементов.
Это позволяет вам вводить html-строку в элементы в одну строку, включая свойства, и даже позволяет вводить составные и множественные элементы. Его скорость оптимизирована - в тесте Mozilla она в 150 раз быстрее.
Если кто-то скажет вам, что это не кросс-браузер, это настолько полезно, что он является стандартом HTML5 и доступен во всех браузерах.
Никогда не пиши elm.innerHTML+=
снова.
Ответ 3
Альтернативой является .createElement()
, .textContent
и .appendChild()
. Добавление с помощью +=
является проблемой только в том случае, если вы имеете дело с большим количеством данных.
Демо: http://jsfiddle.net/ThinkingStiff/v6WgG/
Script
var elm = document.getElementById( 'targetID' ),
div = document.createElement( 'div' );
div.textContent = 'goodbye world';
elm.appendChild( div );
HTML
<div id="targetID">hello world</div>
Ответ 4
Если у пользователя более старые версии IE (или, может быть, более новые, они не пробовали), innerHTML на td
вызовет проблемы. Элементы таблицы в IE доступны только для чтения, tsk tsk tsk.
Ответ 5
Я просто усвоил, почему innerHTML плохой, в этом коде ниже, когда вы устанавливаете innerHTML хром теряет событие onclick jsFiddle
var blah = document.getElementById('blah');
var div = document.createElement('button');
div.style['background-color'] = 'black';
div.style.padding = '20px;';
div.style.innerHTML = 'a';
div.onclick = () => { alert('wtf');};
blah.appendChild(div);
// Uncomment this to make onclick stop working
blah.innerHTML += ' this is the culprit';
<div id="blah">
</div>
Ответ 6
Ответ Майка, вероятно, лучший, но еще одно соображение заключается в том, что вы имеете дело со строками. И конкатенация строк в JavaScript может быть очень медленной, особенно в некоторых старых браузерах. Если вы просто конкатенируете небольшие фрагменты из HTML, то это, вероятно, не заметно, но если у вас есть большая часть страницы, которую вы добавляете что-то неоднократно, вы очень хорошо видите заметную паузу в браузере.
Ответ 7
короткий
Если вы измените innerHTML +=...
(обновить содержимое) на innerHTML =...
(восстановить содержимое), вы получите очень быстрый код. Похоже, самой медленной частью +=
является чтение содержимого DOM в виде строки (без преобразования строки в DOM)
Недостаток использования innerHTML
заключается в том, что вы теряете старые обработчики событий содержимого - однако вы можете использовать аргументы тега, чтобы пропустить это, например, <div onclick="yourfunc(event)">
что приемлемо в небольших проектах.
Долго
Я провел тесты производительности ЗДЕСЬ на Chrome, Firefox и Safari (2019 мая) (вы можете запустить их на своем компьютере, но наберитесь терпения - это займет ~ 5 минут)
![enter image description here]()
function up() {
var container = document.createElement('div');
container.id = 'container';
container.innerHTML = "<p>Init <span>!!!</span></p>"
document.body.appendChild(container);
}
function down() {
container.remove()
}
up();
// innerHTML+=
container.innerHTML += "<p>Just first <span>text</span> here</p>";
container.innerHTML += "<p>Just second <span>text</span> here</p>";
container.innerHTML += "<p>Just third <span>text</span> here</p>";
down();up();
// innerHTML += str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML += s;
down();up();
// innerHTML = innerHTML+str
var s=container.innerHTML+'';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;
down();up();
// innerHTML = str
var s="<p>Init <span>!!!</span></p>";
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.innerHTML = s;
down();up();
// insertAdjacentHTML str
var s='';
s += "<p>Just first <span>text</span> here</p>";
s += "<p>Just second <span>text</span> here</p>";
s += "<p>Just third <span>text</span> here</p>";
container.insertAdjacentHTML("beforeend",s);
down();up();
// insertAdjacentHTML
container.insertAdjacentHTML("beforeend","<p>Just first <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just second <span>text</span> here</p>");
container.insertAdjacentHTML("beforeend","<p>Just third <span>text</span> here</p>");
down();up();
// appendChild
var p1 = document.createElement('p');
p1.innerHTML = 'Just first <span>text</span> here';
var p2 = document.createElement('p');
p2.innerHTML = 'Just second <span>text</span> here';
var p3 = document.createElement('p');
p3.innerHTML = 'Just third <span>text</span> here';
container.appendChild(p1);
container.appendChild(p2);
container.appendChild(p3);
b {color: red}
<b>This snippet NOT test anythig - only presents code used in tests</b>