renovations
This commit is contained in:
parent
a6431c3f97
commit
dce565963b
115 changed files with 1433 additions and 1563 deletions
|
@ -1,49 +0,0 @@
|
|||
# Подсказки
|
||||
|
||||
<ul>
|
||||
<li>Проверить поддержку `insertAdjacentHTML` можно так:
|
||||
|
||||
```js
|
||||
if (elem.insertAdjacentHTML) { ... }
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Если этот метод не поддерживается, то сделайте временный элемент, через `innerHTML` поставьте туда `html`, а затем переместите содержимое в `DocumentFragment`. Последнее действие -- вставка в документ.</li>
|
||||
</ul>
|
||||
|
||||
# Решение
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<ul>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
<li>5</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
var ul = document.body.children[0];
|
||||
var li5 = ul.children[2];
|
||||
|
||||
function insertBefore(elem, html) {
|
||||
if (elem.insertAdjacentHTML) {
|
||||
elem.insertAdjacentHTML("beforeBegin", html);
|
||||
} else {
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
var tmp = document.createElement('DIV');
|
||||
|
||||
tmp.innerHTML = html;
|
||||
|
||||
while(tmp.firstChild) {
|
||||
// перенести все узлы во fragment
|
||||
fragment.appendChild(tmp.firstChild);
|
||||
}
|
||||
elem.parentNode.insertBefore(fragment, elem);
|
||||
}
|
||||
}
|
||||
|
||||
insertBefore(li5, "<li>3</li><li>4</li>")
|
||||
</script>
|
||||
```
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
# Вставка insertAdjacentHTML/DocumentFragment
|
||||
|
||||
[importance 4]
|
||||
|
||||
Напишите кроссбраузерную функцию `insertBefore(elem, html)`, которая:
|
||||
|
||||
<ul>
|
||||
<li>Вставляет HTML-строку `html` перед элементом `elem`, используя `insertAdjacentHTML`,</li>
|
||||
<li>Если он не поддерживается (старый Firefox) -- то через `DocumentFragment`.</li>
|
||||
</ul>
|
||||
|
||||
В обоих случаях должна быть лишь одна операция с DOM документа.
|
||||
|
||||
Следующий код должен вставить два пропущенных элемента списка `<li>3</li><li>4</li>`:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
<li>5</li>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
var ul = document.body.children[0];
|
||||
var li5 = ul.children[2];
|
||||
|
||||
function insertBefore(elem, html) {
|
||||
/* ваш код */
|
||||
}
|
||||
|
||||
insertBefore(li5, "<li>3</li><li>4</li>")
|
||||
</script>
|
||||
```
|
||||
|
|
@ -8,4 +8,4 @@
|
|||
|
||||
P.S. Создавать `DocumentFragment` здесь ни к чему. Можно вытащить из документа `TBODY` и иметь дело с ним в отрыве от DOM (алгоритм 4).
|
||||
|
||||
P.P.S. Если нужно сделать много узлов, то обычно `innerHTML` работает быстрее, чем генерация элементов через DOM-вызовы. Но в данном случае мы не создаём элементы, а сортируем и перевставляем готовые, так что результаты могут отличаться.
|
||||
P.P.S. Если нужно сделать много узлов, то обычно `innerHTML` работает быстрее, чем удаление и вставка элементов через DOM-вызовы. То есть, сгенерировать таблицу заново эффективнее.
|
|
@ -44,4 +44,5 @@
|
|||
Как сделать, чтобы сортировка работала как можно быстрее? А если в таблице 10000 строк (бывает и такое)?
|
||||
|
||||
P.S. Может ли здесь помочь `DocumentFragment`?
|
||||
|
||||
P.P.S. Если предположить, что у нас заранее есть массив данных для таблицы в JavaScript -- что быстрее: отсортировать эту таблицу или сгенерировать новую?
|
|
@ -31,7 +31,7 @@ document.body.appendChild(ul); // затем в документ
|
|||
|
||||
Как ни странно, между этими последовательностями есть разница. В большинстве браузеров, второй вариант -- быстрее.
|
||||
|
||||
Почему же? Иногда говорят: "потому что браузер перерисовывает каждый раз при добавлении элемента". Это не так. **Дело вовсе не в перерисовке**.
|
||||
Почему же? Иногда говорят: "потому что браузер перерисовывает каждый раз при добавлении элемента". Это не так. Дело вовсе не в перерисовке.
|
||||
|
||||
Браузер достаточно "умён", чтобы ничего не перерисовывать понапрасну. В большинстве случаев процессы перерисовки и сопутствующие вычисления будут отложены до окончания работы скрипта, и на тот момент уже совершенно без разницы, в какой последовательности были изменены узлы.
|
||||
|
||||
|
@ -39,6 +39,7 @@ document.body.appendChild(ul); // затем в документ
|
|||
|
||||
Что именно происходит -- зависит от конкретной, внутренней браузерной реализации DOM, но это отнимает время. Конечно, браузеры развиваются и стараются свести лишние действия к минимуму.
|
||||
|
||||
[online]
|
||||
### Бенчмарк [#insert-bench-tbody]
|
||||
|
||||
Чтобы легко проверить текущее состояние дел -- вот два бенчмарка.
|
||||
|
@ -53,10 +54,9 @@ document.body.appendChild(ul); // затем в документ
|
|||
|
||||
<table id="bench-table"></table>
|
||||
|
||||
```js
|
||||
//+ hide="открыть код" src="insert-bench.js"
|
||||
```
|
||||
Код для тестов находится в файле [](insert-bench.js).
|
||||
|
||||
[/online]
|
||||
## Добавление множества узлов
|
||||
|
||||
Продолжим работать со вставкой узлов.
|
||||
|
@ -71,7 +71,7 @@ document.body.appendChild(ul); // затем в документ
|
|||
ul.innerHTML += "<li>1</li><li>2</li>...";
|
||||
```
|
||||
|
||||
Но операция `+=` с `innerHTML` не работает с DOM. Она не прибавляет, а заменяет всё содержимое списка на дополненную строку. Это не только медленно, но все внешние ресурсы (картинки) будут загружены заново! Так лучше не делать.
|
||||
Но операция `+=` с `innerHTML` не работает с DOM. Она не прибавляет, а заменяет всё содержимое списка на дополненную строку. Это не только медленно, но все внешние ресурсы (картинки) будут загружены заново. Так лучше не делать.
|
||||
|
||||
А если нужно вставить в середину списка? Здесь `innerHTML` вообще не поможет.
|
||||
|
||||
|
@ -122,9 +122,9 @@ li5.insertAdjacentHTML("beforeBegin", "<li>3</li><li>4</li>");
|
|||
</script>
|
||||
```
|
||||
|
||||
Единственный недостаток этого метода -- он не работает в Firefox до версии 8. Но его можно легко добавить, используя следующий JavaScript: [insertAdjacentFF.js](/files/tutorial/browser/dom/insertAdjacentFF.js).
|
||||
Единственный недостаток этого метода -- он не работает в Firefox до версии 8. Но его можно легко добавить, используя [полифилл insertAdjacentHTML для Firefox](insertAdjacentHTML.js).
|
||||
|
||||
У этого метода есть "близнецы-братья", которые поддерживаются везде, кроме FF, но в него они добавляются этим же скриптом:
|
||||
У этого метода есть "близнецы-братья", которые поддерживаются везде, кроме Firefox, но в него они добавляются тем же полифиллом:
|
||||
|
||||
<ul>
|
||||
<li>[elem.insertAdjacentElement(where, newElem)](http://help.dottoro.com/ljbreokf.php) -- вставляет в произвольное место не строку HTML, а элемент `newElem`.</li>
|
||||
|
@ -136,7 +136,7 @@ li5.insertAdjacentHTML("beforeBegin", "<li>3</li><li>4</li>");
|
|||
## DocumentFragment
|
||||
|
||||
[warn header="Важно для старых браузеров"]
|
||||
Оптимизация, о которой здесь идёт речь, важна в первую очередь для старых браузеров, включая IE9-. В современных браузерах эффект от нее, как правило, не превышает 20%, а иногда может быть и отрицательным.
|
||||
Оптимизация, о которой здесь идёт речь, важна в первую очередь для старых браузеров, включая IE9-. В современных браузерах эффект от нее, как правило, небольшой, а иногда может быть и отрицательным.
|
||||
[/warn]
|
||||
|
||||
До этого мы говорили о вставке строки в DOM. А что делать в случае, когда надо в существующий `UL` вставить много *DOM-элементов*?
|
||||
|
@ -165,9 +165,9 @@ fragment.cloneNode(true); // клонирование с подэлемента
|
|||
|
||||
**У `DocumentFragment` нет обычных свойств DOM-узлов, таких как `innerHTML`, `tagName` и т.п. Это не узел.**
|
||||
|
||||
**"Фишка" заключается в том, что когда `DocumentFragment` вставляется в DOM -- то он исчезает, а вместо него вставляются его дети. Это свойство является уникальной особенностью `DocumentFragment`.**
|
||||
Его "Фишка" заключается в том, что когда `DocumentFragment` вставляется в DOM -- то он исчезает, а вместо него вставляются его дети. Это свойство является уникальной особенностью `DocumentFragment`.
|
||||
|
||||
Например, если добавить в него много `LI`, и потом `appendChild` к `UL`, то фрагмент растворится, и в DOM вставятся именно `LI`, причём в том же порядке, в котором были во фрагменте.
|
||||
Например, если добавить в него много `LI`, и потом вызвать `ul.appendChild(fragment)`, то фрагмент растворится, и в DOM вставятся именно `LI`, причём в том же порядке, в котором были во фрагменте.
|
||||
|
||||
Псевдокод:
|
||||
|
||||
|
@ -184,35 +184,22 @@ for (цикл по li) {
|
|||
ul.appendChild(fragment); // вместо фрагмента вставятся элементы списка
|
||||
```
|
||||
|
||||
В современных браузерах эффект от такой оптимизации может быть различным. Чтобы понять текущее положение вещей, попробуйте в различных браузерах следующий небольшой бенчмарк.
|
||||
В современных браузерах эффект от такой оптимизации может быть различным, а на небольших документах иногда и отрицательным.
|
||||
|
||||
При нажатии на кнопки ниже в список добавляются `100` элементов.
|
||||
[pre]
|
||||
<div>
|
||||
<input type="button" onclick="alert(bench(DocumentFragmentTest.insertPlain,200))" value="Обычная вставка"/>
|
||||
<input type="button" onclick="alert(bench(DocumentFragmentTest.insertDocumentFragment,200))" value="Вставка через DocumentFragment">
|
||||
</div>
|
||||
[/pre]
|
||||
<ul id="bench-list"></ul>
|
||||
Понять текущее положение вещей вы можете, запустив следующий [edit src="benchmark"]небольшой бенчмарк[/edit].
|
||||
|
||||
|
||||
<script src="/files/tutorial/browser/dom/documentfragment-bench.js"></script>
|
||||
|
||||
```js
|
||||
//+ hide="открыть код" src="documentfragment-bench.js"
|
||||
```
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>**Манипуляции, меняющие структуру DOM (вставка, удаление элементов), как правило, быстрее с отдельным маленьким узлом, чем с большим DOM, который находится в документе.**
|
||||
<li>Манипуляции, меняющие структуру DOM (вставка, удаление элементов), как правило, быстрее с отдельным маленьким узлом, чем с большим DOM, который находится в документе.
|
||||
|
||||
Конкретная разница зависит от внутренней реализации DOM в браузере.</li>
|
||||
<li>**Семейство методов `elem.insertAdjacentHTML(where, html)`, `insertAdjacentElement`, `insertAdjacentText` позволяет вставлять HTML/элемент/текст в произвольное место документа.**
|
||||
<li>Семейство методов `elem.insertAdjacentHTML(where, html)`, `insertAdjacentElement`, `insertAdjacentText` позволяет вставлять HTML/элемент/текст в произвольное место документа.
|
||||
|
||||
Метод `insertAdjacentHTML` не поддерживается в Firefox до версии 8, остальные два метода не поддерживаются в Firefox, на момент написания текста, вообще, но есть небольшой скрипт [insertAdjacentFF.js](/files/tutorial/browser/dom/insertAdjacentFF.js), который добавляет их. Конечно, он нужен только для Firefox.
|
||||
Метод `insertAdjacentHTML` не поддерживается в Firefox до версии 8, остальные два метода не поддерживаются в Firefox, на момент написания текста, вообще, но есть небольшой скрипт [insertAdjacentFF.js](insertAdjacentFF.js), который добавляет их. Конечно, он нужен только для Firefox.
|
||||
</li>
|
||||
<li>**`DocumentFragment` позволяет минимизировать количество вставок в большой живой DOM. Эта оптимизация особо эффективна в старых браузерах, в новых эффект от неё меньше.**
|
||||
<li>`DocumentFragment` позволяет минимизировать количество вставок в большой живой DOM. Эта оптимизация особо эффективна в старых браузерах, в новых эффект от неё меньше или наоборот отрицательный.
|
||||
|
||||
Элементы сначала вставляются в него, а потом -- он вставляется в DOM. При вставке `DocumentFragment` "растворяется", и вместо него вставляются содержащиеся в нём узлы.
|
||||
|
||||
|
@ -296,11 +283,11 @@ var appendLast = new function() {
|
|||
</script>
|
||||
|
||||
<style>
|
||||
##bench-table td {
|
||||
#bench-table td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
##bench-list li {
|
||||
#bench-list li {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
|
|
12
2-ui/1-document/12-multi-insert/benchmark.view/bench.js
Normal file
12
2-ui/1-document/12-multi-insert/benchmark.view/bench.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
|
||||
function bench(test, times) {
|
||||
var sum = 0;
|
||||
for(var i=0; i<times; i++) {
|
||||
if(test.setup) test.setup();
|
||||
var t = new Date();
|
||||
test.work();
|
||||
sum += (new Date() - t);
|
||||
if(test.tearDown) test.tearDown();
|
||||
}
|
||||
return sum;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
var DocumentFragmentTest = new function() {
|
||||
var benchList = document.getElementById('bench-list');
|
||||
|
||||
var items = [];
|
||||
for(var i=0; i<100; i++) {
|
||||
var li = document.createElement('li');
|
||||
li.innerHTML = i;
|
||||
items.push(li);
|
||||
}
|
||||
|
||||
this.insertPlain = new function() {
|
||||
|
||||
this.setup = function() {
|
||||
while(benchList.firstChild) {
|
||||
benchList.removeChild(benchList.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
this.work = function() {
|
||||
for(var i=0; i<items.length; i++) {
|
||||
benchList.appendChild(items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.insertDocumentFragment = new function() {
|
||||
|
||||
this.setup = function() {
|
||||
// очистить всё
|
||||
while(benchList.firstChild) {
|
||||
benchList.removeChild(benchList.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
this.work = function() {
|
||||
var docFrag = document.createDocumentFragment();
|
||||
for(var i=0; i<items.length; i++) {
|
||||
docFrag.appendChild(items[i]);
|
||||
}
|
||||
benchList.appendChild(docFrag);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
20
2-ui/1-document/12-multi-insert/benchmark.view/index.html
Normal file
20
2-ui/1-document/12-multi-insert/benchmark.view/index.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Вставляются 100 элементов LI в пустой UL.
|
||||
|
||||
<input type="button" onclick="alert(bench(DocumentFragmentTest.insertPlain,200))" value="Замерить время на обычную вставку"/>
|
||||
|
||||
<input type="button" onclick="alert(bench(DocumentFragmentTest.insertDocumentFragment,200))" value="Замерить время на вставку через DocumentFragment">
|
||||
|
||||
<ul id="bench-list"></ul>
|
||||
|
||||
<script src="bench.js"></script>
|
||||
<script src="documentfragment-bench.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
35
2-ui/1-document/12-multi-insert/insertAdjacentFF.js
Normal file
35
2-ui/1-document/12-multi-insert/insertAdjacentFF.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
// http://learn.javascript.ru/files/tutorial/browser/dom/insertAdjacentFF.js
|
||||
// Добавляет поддержку insertAdjacent* в Firefox
|
||||
|
||||
if (typeof HTMLElement != "undefined" && !HTMLElement.prototype.insertAdjacentElement) {
|
||||
HTMLElement.prototype.insertAdjacentElement = function(where, parsedNode) {
|
||||
switch(where) {
|
||||
case 'beforeBegin':
|
||||
this.parentNode.insertBefore(parsedNode, this)
|
||||
break;
|
||||
case 'afterBegin':
|
||||
this.insertBefore(parsedNode, this.firstChild);
|
||||
break;
|
||||
case 'beforeEnd':
|
||||
this.appendChild(parsedNode);
|
||||
break;
|
||||
case 'afterEnd':
|
||||
if(this.nextSibling) this.parentNode.insertBefore(parsedNode, this.nextSibling);
|
||||
else this.parentNode.appendChild(parsedNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HTMLElement.prototype.insertAdjacentHTML = function(where, htmlStr) {
|
||||
var r = this.ownerDocument.createRange();
|
||||
r.setStartBefore(this);
|
||||
var parsedHTML = r.createContextualFragment(htmlStr);
|
||||
this.insertAdjacentElement(where, parsedHTML)
|
||||
}
|
||||
|
||||
|
||||
HTMLElement.prototype.insertAdjacentText = function(where, txtStr) {
|
||||
var parsedText = document.createTextNode(txtStr)
|
||||
this.insertAdjacentElement(where, parsedText)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue