renovations
This commit is contained in:
parent
26008151b9
commit
3af3564921
15 changed files with 231 additions and 55 deletions
|
@ -6,7 +6,7 @@
|
|||
|
||||
[cut]
|
||||
|
||||
## Метод contains
|
||||
## Метод contains для проверки на вложенность
|
||||
|
||||
Синтаксис:
|
||||
|
||||
|
@ -16,7 +16,7 @@ var result = parent.contains(child);
|
|||
|
||||
Возвращает `true`, если `parent` содержит `child` или `parent == child`.
|
||||
|
||||
## Метод compareDocumentPosition
|
||||
## Метод compareDocumentPosition для порядка узлов
|
||||
|
||||
Бывает, что у нас есть два элемента, к примеру, `<li>` в списке, и нужно понять, какой из них выше другого.
|
||||
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
Родителя `parentNode` можно получить из `elem`.
|
||||
|
||||
Нужно учесть два момента.
|
||||
<ol>
|
||||
<li>Родителя может не быть (элемент уже удален или еще не вставлен). В этом случае мы не будем ничего делать.</li>
|
||||
<li>Для совместимости со стандартным методом нужно вернуть удаленный элемент.</li>
|
||||
</ol>
|
||||
|
||||
Вот так выглядит решение:
|
||||
|
||||
```js
|
||||
function remove(elem) {
|
||||
var parent = elem.parentNode;
|
||||
if (parent) elem.parentNode.removeChild(elem);
|
||||
return elem;
|
||||
}
|
||||
```
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# Удаление элементов
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите функцию, которая удаляет элемент из DOM.
|
||||
|
||||
Синтаксис должен быть таким: `remove(elem)`, то есть, более короткий вариант, чем `parentNode.removeChild(elem)`.
|
||||
|
||||
```html
|
||||
<div>Это</div>
|
||||
<div>Все</div>
|
||||
<div>Элементы DOM</div>
|
||||
|
||||
<script>
|
||||
var elem = document.body.children[0];
|
||||
|
||||
function remove(elem) { /* ваш код */ }
|
||||
*!*
|
||||
remove(elem); // <-- функция должна удалить элемент
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
Родителя `parentNode` можно получить из `elem`.
|
||||
|
||||
Вот так выглядит решение:
|
||||
```html
|
||||
//+ run
|
||||
<div>Это</div>
|
||||
<div>Все</div>
|
||||
<div>Элементы DOM</div>
|
||||
|
||||
<script>
|
||||
if (!Element.prototype.remove) {
|
||||
Element.prototype.remove = function remove() {
|
||||
if (this.parentNode) {
|
||||
this.parentNode.removeChild(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var elem = document.body.children[0];
|
||||
|
||||
elem.remove();
|
||||
</script>
|
||||
```
|
|
@ -0,0 +1,28 @@
|
|||
# Удаление элементов
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите полифилл для метода `remove` для старых браузеров.
|
||||
|
||||
Вызов `elem.remove()`:
|
||||
<ul>
|
||||
<li>Если у `elem` нет родителя -- ничего не делает.</li>
|
||||
<li>Если есть -- удаляет элемент из родителя.</li>
|
||||
</ul>
|
||||
|
||||
```html
|
||||
<div>Это</div>
|
||||
<div>Все</div>
|
||||
<div>Элементы DOM</div>
|
||||
|
||||
<script>
|
||||
/* ваш код полифилла */
|
||||
|
||||
var elem = document.body.children[0];
|
||||
|
||||
*!*
|
||||
elem.remove(); // <-- вызов должен удалить элемент
|
||||
*/!*
|
||||
</script>
|
||||
```
|
||||
|
|
@ -270,6 +270,13 @@ div.parentNode.insertBefore( div2, div.nextSibling );
|
|||
[/smart]
|
||||
|
||||
|
||||
[smart header="Метод `remove`"]
|
||||
|
||||
В современном стандарте есть также метод [elem.remove()](https://dom.spec.whatwg.org/#dom-childnode-remove), который удаляет элемент напрямую, не требуя ссылки на родителя. Это зачастую удобнее, чем `removeChild`.
|
||||
|
||||
Он поддерживается во всех современных браузерах, кроме IE11-. Впрочем, легко подключить или даже сделать полифилл.
|
||||
[/smart]
|
||||
|
||||
### Удаление сообщения
|
||||
|
||||
Сделаем так, что через секунду сообщение пропадёт:
|
||||
|
@ -326,6 +333,7 @@ div.parentNode.insertBefore( div2, div.nextSibling );
|
|||
|
||||
В современных браузерах (кроме IE8-) в качестве альтернативы можно использовать присвоение `textContent`.
|
||||
|
||||
|
||||
## Итого
|
||||
|
||||
Методы для создания узлов:
|
||||
|
|
|
@ -188,6 +188,53 @@ ul.appendChild(fragment); // вместо фрагмента вставятс
|
|||
|
||||
Понять текущее положение вещей вы можете, запустив следующий [edit src="benchmark"]небольшой бенчмарк[/edit].
|
||||
|
||||
## append/prepend, before/after, replaceWith
|
||||
|
||||
Сравнительно недавно в [стандарте](https://dom.spec.whatwg.org/) появились методы, которые позволяют вставить что угодно и куда угодно.
|
||||
|
||||
Синтаксис:
|
||||
|
||||
<ul>
|
||||
<li>`node.append(...nodes)` -- вставляет `nodes` в конец `node`,</li>
|
||||
<li>`node.prepend(...nodes)` -- вставляет `nodes` в начало `node`,</li>
|
||||
<li>`node.after(...nodes)` -- вставляет `nodes` после узла `node`,</li>
|
||||
<li>`node.before(...nodes)` -- вставляет `nodes` перед узлом `node`,</li>
|
||||
<li>`node.replaceWith(...nodes)` -- вставляет `nodes` вместо `node`.</li>
|
||||
</ul>
|
||||
|
||||
Эти методы ничего не возвращают.
|
||||
|
||||
Во всех этих методах `nodes` -- DOM-узлы или строки, в любом сочетании и количестве. Причём строки вставляются именно как текстовые узлы, в отличие от `insertAdjacentHTML`.
|
||||
|
||||
|
||||
Пример (с полифиллом):
|
||||
```html
|
||||
<!--+ run autorun height=80 -->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="//polyfill.webservices.ft.com/v1/polyfill.js?features=Element.prototype.mutation"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
// добавим элемент в конец <body>
|
||||
var p = document.createElement('p');
|
||||
document.body.append(p);
|
||||
|
||||
var em = document.createElement('em');
|
||||
em.append('Мир!');
|
||||
|
||||
// вставить в параграф текстовый и обычный узлы
|
||||
p.append("Привет, ", em);
|
||||
|
||||
// добавить элемент после <p>
|
||||
p.after(document.createElement('hr'))
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Итого
|
||||
|
||||
|
@ -195,9 +242,14 @@ ul.appendChild(fragment); // вместо фрагмента вставятс
|
|||
<li>Манипуляции, меняющие структуру DOM (вставка, удаление элементов), как правило, быстрее с отдельным маленьким узлом, чем с большим DOM, который находится в документе.
|
||||
|
||||
Конкретная разница зависит от внутренней реализации DOM в браузере.</li>
|
||||
<li>Семейство методов `elem.insertAdjacentHTML(where, html)`, `insertAdjacentElement`, `insertAdjacentText` позволяет вставлять HTML/элемент/текст в произвольное место документа.
|
||||
<li>Семейство методов для вставки HTML/элемента/текста в произвольное место документа:
|
||||
<ul>
|
||||
<li>`elem.insertAdjacentHTML(where, html)`</li>
|
||||
<li>`elem.insertAdjacentElement(where, node)`</li>
|
||||
<li>`elem.insertAdjacentText(where, text)`</li>
|
||||
</ul>
|
||||
|
||||
Метод `insertAdjacentHTML` не поддерживается в Firefox до версии 8, остальные два метода не поддерживаются в Firefox, на момент написания текста, вообще, но есть небольшой скрипт [insertAdjacentFF.js](insertAdjacentFF.js), который добавляет их. Конечно, он нужен только для Firefox.
|
||||
Два последних метода не поддерживаются в Firefox, на момент написания текста, но есть небольшой полифилл [insertAdjacentFF.js](insertAdjacentFF.js), который добавляет их. Конечно, он нужен только для Firefox.
|
||||
</li>
|
||||
<li>`DocumentFragment` позволяет минимизировать количество вставок в большой живой DOM. Эта оптимизация особо эффективна в старых браузерах, в новых эффект от неё меньше или наоборот отрицательный.
|
||||
|
||||
|
@ -205,6 +257,14 @@ ul.appendChild(fragment); // вместо фрагмента вставятс
|
|||
|
||||
`DocumentFragment`, в отличие от `insertAdjacent*`, работает с коллекцией DOM-узлов.
|
||||
</li>
|
||||
<li>Современные методы, работают с любым количеством узлов и текста, желателен полифилл:
|
||||
<ul>
|
||||
<li>`append/prepend` -- вставка в конец/начало.</li>
|
||||
<li>`before/after` -- вставка после/перед.</li>
|
||||
<li>`replaceWith` -- замена.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Поиск: getElement* и querySelector*
|
||||
# Поиск: getElement* и querySelector* и не только
|
||||
|
||||
Прямая навигация от родителя к потомку удобна, если элементы рядом. А если нет?
|
||||
|
||||
|
@ -191,11 +191,11 @@ alert( articles.length ); // 2, найдёт оба элемента
|
|||
|
||||
## querySelectorAll [#querySelectorAll]
|
||||
|
||||
Вызов `elem.querySelectorAll(cssQuery)` возвращает все элементы внутри `elem`, удовлетворяющие CSS-селектору `cssQuery`.
|
||||
Вызов `elem.querySelectorAll(css)` возвращает все элементы внутри `elem`, удовлетворяющие CSS-селектору `css`.
|
||||
|
||||
Это один из самых часто используемых и полезных методов при работе с DOM.
|
||||
|
||||
Он есть во всех современных браузерах, включая IE8+ (IE8 должен быть в режиме соответствия стандарту).
|
||||
Он есть во всех современных браузерах, включая IE8+ (в режиме соответствия стандарту).
|
||||
|
||||
Следующий запрос получает все элементы `LI`, которые являются последними потомками своих `UL`:
|
||||
|
||||
|
@ -222,9 +222,9 @@ alert( articles.length ); // 2, найдёт оба элемента
|
|||
|
||||
## querySelector [#querySelector]
|
||||
|
||||
Вызов `elem.querySelector(cssQuery)` возвращает первый элемент, соответствующий CSS-селектору `cssQuery`.
|
||||
Вызов `elem.querySelector(css)` возвращает первый элемент, соответствующий CSS-селектору `css`.
|
||||
|
||||
Иначе говоря, результат -- такой же, как и при `elem.querySelectorAll(cssQuery)[0]`, но в последнем вызове сначала ищутся все элементы, а потом берётся первый, а в `elem.querySelector(cssQuery)` ищется только первый, то есть он эффективнее.
|
||||
Иначе говоря, результат -- такой же, как и при `elem.querySelectorAll(css)[0]`, но в последнем вызове сначала ищутся все элементы, а потом берётся первый, а в `elem.querySelector(css)` ищется только первый, то есть он эффективнее.
|
||||
|
||||
## matches
|
||||
|
||||
|
@ -258,6 +258,37 @@ alert( articles.length ); // 2, найдёт оба элемента
|
|||
</script>
|
||||
```
|
||||
|
||||
## closest
|
||||
|
||||
Метод `elem.closest(css)` ищет ближайшего предка, подходящего под CSS-селектор `css`.
|
||||
|
||||
Он самый новый из методов, рассмотренных в этой главе, поэтому не все браузеры его поддерживают. Это, конечно, легко поправимо, как мы увидим позже в главе [](/dom-polyfill).
|
||||
|
||||
Пример использования:
|
||||
|
||||
|
||||
```html
|
||||
<!--+ run -->
|
||||
<ul>
|
||||
<li class="chapter">Глава I
|
||||
<ul>
|
||||
<li class="subchapter">Глава <span class="num">1.1</span></li>
|
||||
<li class="subchapter">Глава <span class="num">1.2</span></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<script>
|
||||
var numberSpan = document.querySelector('.num');
|
||||
|
||||
// браузер должен поддерживать этот метод
|
||||
alert( numberSpan.closest('li').className ) // subchapter
|
||||
|
||||
alert( numberSpan.closest('.chapter').tagName ) // LI
|
||||
</script>
|
||||
```
|
||||
|
||||
## XPath в современных браузерах
|
||||
|
||||
Для полноты картины рассмотрим ещё один способ поиска, который обычно используется в XML. Это <a href="http://www.w3.org/TR/xpath/">язык запросов XPath</a>.
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
Код для полифилла здесь особенно прост.
|
||||
|
||||
Реализовывать ничего не надо, просто записать нужный метод в `Element.prototype.matches`, если его там нет:
|
||||
|
||||
```js
|
||||
(function() {
|
||||
|
||||
// проверяем поддержку
|
||||
if (!Element.prototype.matches) {
|
||||
|
||||
// определяем свойство
|
||||
Element.prototype.matches = Element.prototype.matchesSelector ||
|
||||
Element.prototype.webkitMatchesSelector ||
|
||||
Element.prototype.mozMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector,
|
||||
|
||||
}
|
||||
|
||||
})();
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Полифилл для matches
|
||||
|
||||
[importance 5]
|
||||
|
||||
Метод `elem.matches(css)` в некоторых старых браузерах поддерживается под старым именем `matchesSelector` или с префиксами, то есть: `webkitMatchesSelector` (старый Chrome, Safari), `mozMatchesSelector` (старый Firefox) или `Element.prototype.msMatchesSelector` (старый IE).
|
||||
|
||||
Создайте полифилл, который гарантирует стандартный синтаксис `elem.matches(css)` для всех браузеров.
|
|
@ -0,0 +1,24 @@
|
|||
Код для этого полифилла имеет стандартный вид:
|
||||
|
||||
```js
|
||||
(function() {
|
||||
|
||||
// проверяем поддержку
|
||||
if (!Element.prototype.closest) {
|
||||
|
||||
// реализуем
|
||||
Element.prototype.closest = function(css) {
|
||||
var node = this;
|
||||
|
||||
while (node) {
|
||||
if (node.matches(selector)) return node;
|
||||
else node = node.parentElement;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
})();
|
||||
```
|
||||
|
||||
Обратим внимание, что код этого полифилла использует `node.matches`, то есть может в свою очередь потребовать полифилла для него. Это типичная ситуация -- один полифилл тянет за собой другой. Именно поэтому сервисы и библиотеки полифиллов очень полезны.
|
|
@ -0,0 +1,7 @@
|
|||
# Полифилл для closest
|
||||
|
||||
[importance 5]
|
||||
|
||||
Метод `elem.closest(css)` для поиска ближайшего родителя, удовлетворяющего селектору `css`, не поддерживается некоторыми браузерами, например IE11-.
|
||||
|
||||
Создайте для него полифилл.
|
|
@ -1,9 +1,9 @@
|
|||
# Полифилл для textContent
|
||||
|
||||
[importance 5]
|
||||
[importance 3]
|
||||
|
||||
Свойство `textContent` не поддерживается IE8. Однако, там есть свойство `innerText`.
|
||||
|
||||
Создаёте полифилл для него, который использует `innerText`. При наличии полифилла в IE8 свойство `textContent` должно стать "псевдонимом" для `innerText`.
|
||||
Создаёте полифилл, который проверяет поддержку свойства `textContent`, и если её нет -- создаёт его, используя `innerText`. Получится, что в IE8 "новое" свойство `textContent` будет "псевдонимом" для `innerText`.
|
||||
|
||||
Хотя свойство `innerText` и работает по-иному, нежели `textContent`, но в некоторых ситуациях они могут быть взаимозаменимы. Именно на них направлен полифилл.
|
|
@ -56,7 +56,7 @@ if( document.documentElement.firstElementChild === undefined ) { // (1)
|
|||
|
||||
## Проверка встроенного свойства
|
||||
|
||||
Для проверки встроенной поддержки `firstElementChild` мы можем просто обратиться к `document.documentElement`.
|
||||
Для проверки встроенной поддержки `firstElementChild` мы можем просто обратиться к `document.documentElement.firstElementChild`.
|
||||
|
||||
Если DOM-свойство `firstElementChild` поддерживается, то его значение не может быть `undefined`. Если детей нет -- свойство равно `null`, но не `undefined`.
|
||||
|
||||
|
@ -163,9 +163,17 @@ alert( document.body.lowerTag ); // body
|
|||
В IE6,7 геттеры/сеттеры совсем не работают. Когда-то для них использовалась особая "IE-магия" при помощи `.htc`-файлов, которые [более не поддерживаются](http://msdn.microsoft.com/en-us/library/ie/hh801216.aspx). Если нужно поддерживать и эти версии, то рекомендуется воспользоваться фреймворками. К счастью, для большинства проектов эти браузеры уже стали историей.
|
||||
[/warn]
|
||||
|
||||
## А есть ли поддержка?
|
||||
|
||||
А нужен ли вообще полифилл? Какие браузеры поддерживают свойство или метод?
|
||||
|
||||
Зачастую такая информация есть в справочнике MDN, например для метода `remove()`: [](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode.remove) -- табличка совместимости внизу.
|
||||
|
||||
Также бывает полезен сервис [](http://caniuse.com), например для `elem.matches(css)`: [](http://caniuse.com/#feat=matchesselector).
|
||||
|
||||
## Итого
|
||||
|
||||
Если вы поддерживаете устаревшие браузеры, нет смысла ограничивать себя в использовании современных возможностей.
|
||||
Если вы поддерживаете устаревшие браузеры -- и здесь речь идёт не только про старые IE, другие браузеры тоже обновляются не у всех мгновенно -- не обязательно ограничивать себя в использовании современных возможностей.
|
||||
|
||||
Многие из них легко полифиллятся добавлением на страницу соответствующих библиотек.
|
||||
|
||||
|
@ -193,7 +201,6 @@ alert( document.body.lowerTag ); // body
|
|||
|
||||
При запросе сервис анализирует заголовки, понимает, какая версия какого браузера к нему обратилась и возвращает скрипт-полифилл, добавляющий в браузер возможности, которых там нет. В параметре `features` можно указать, какие именно возможности нужны, в примере выше это функции стандарта ES6. Подробнее -- см. [примеры](http://polyfill.webservices.ft.com/v1/docs/examples) и [список возможностей](http://polyfill.webservices.ft.com/v1/docs/features/).
|
||||
|
||||
|
||||
Также есть и другие коллекции, как правило, полифиллы организованы в виде коллекции, из которой можно как выбрать отдельные свойства и функции, так и подключить всё вместе, пачкой.
|
||||
|
||||
Примеры полифиллов:
|
||||
|
@ -213,3 +220,4 @@ alert( document.body.lowerTag ); // body
|
|||
|
||||
Конечно, можно собрать и свою библиотеку полифиллов самостоятельно из различных коллекций, которые перечислены выше, а при необходимости и написать самому. В этой части учебника мы изучим ещё много методов работы с DOM, которые в этом помогут.
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue