Когда был выпущен jQuery, одной из главных причин его стремительного роста популярности была легкость, с которой он мог выбирать элементы DOM, проходить по ним и модифицировать их контент. Но это было в далеком 2006 году. В те дни мы были привязаны к Internet Explorer 7, а ECMAScript 5 оставался на пару лет раньше.
К счастью, с тех пор многое изменилось. Браузеры стали значительно более совместимыми со стандартами, а нативный JavaScript улучшился как на дрожжах. И когда ситуация улучшилась, мы увидели, как люди спрашивают , нужен ли нам jQuery . Я не буду вдаваться в этот аргумент, скорее, я хотел бы предложить пищу для размышлений, поскольку я представляю шесть собственных методов манипулирования DOM, которые были вдохновлены этой замечательной библиотекой. Это следующие:
В этой статье я хочу изучить сходство и различия между этими собственными методами манипулирования DOM и их аналогами jQuery. Тогда, надеюсь, в следующий раз, когда вы обнаружите, что включили jQuery ради удобного метода или двух, вы можете вместо этого использовать мощь ванильного JavaScript.
1. append ()
Метод append выполняет операцию вставки, то есть добавляет узлы в дерево DOM. Как видно из названия, оно добавляет переданные аргументы в список дочерних элементов узла, на котором оно вызывается. Рассмотрим следующий пример:
const parent = document.createElement('div')
const child1 = document.createElement('h1')
parent.append(child1)
parent.outerHTML
// <div><h1></h1></div>
const child2 = document.createElement('h2')
parent.append(child2)
parent.outerHTML
// <div><h1></h1><h2></h2></div>
На данный момент вы будете прощены за вопрос, чем это отличается от родного метода appendChild . Ну, первое различие заключается в том, что append()
добавляться в список дочерних элементов, как и метод добавления jQuery . Продолжая предыдущий фрагмент:
const child3 = document.createElement('p')
const child4 = document.createElement('p')
const child5 = document.createElement('p')
parent.append(child3, child4, child5)
parent.outerHTML
/* Outputs:
<div>
<h1></h1>
<h2></h2>
<p></p>
<p></p>
<p></p>
</div>
*/
Кроме того, аргумент может быть даже строкой. Итак, в то время как с appendChild()
parent.appendChild(document.createTextNode('just some text'))
с append()
parent.append('just some text')
parent.outerHTML
/* Outputs:
<div>
<h1></h1>
<h2></h2>
<p></p>
<p></p>
<p></p>
just some text
</div>
*/
Строка преобразуется в текстовый узел , поэтому любой HTML не анализируется:
parent.append('<p>foo</p>')
parent.outerHTML
/* Outputs:
<div>
<h1></h1>
<h2></h2>
<p></p>
<p></p>
<p></p>
just some text
<p>foo</p>
</div>
*/
Это в отличие от метода jQuery , где строки разметки анализируются, а соответствующие узлы генерируются и вставляются в дерево DOM.
Как обычно бывает, если добавленный узел уже присутствует в дереве, он сначала удаляется со своей старой позиции:
const myParent = document.createElement('div')
const child = document.createElement('h1')
myParent.append(child)
const myOtherParent = document.createElement('div')
const myOtherParent.append(child)
myOtherParent.outerHTML
// <div><h1></h1></div>
myParent.outerHTML
// <div></div>"
Последнее различие между append()
appendChild()
undefined
2. prepend ()
Метод prepend очень похож на append()
Потомки добавляются, но на этот раз они добавляются в список потомков узла, на котором вызывается метод, непосредственно перед первым потомком:
const parent = document.createElement('div')
const child1 = document.createElement('p')
parent.prepend(child1)
parent.outerHTML
// <div><p></p></div>
const child2 = document.createElement('h2')
parent.prepend('just some text', child2)
parent.outerHTML
/* Outputs:
<div>
just some text
<h2></h2>
<p></p>
</div>
*/
Возвращаемое значение метода не undefined
Соответствующий метод jQuery — prepend () .
3. после ()
Метод after — это еще один метод вставки, но на этот раз он должен вызываться на дочернем узле, то есть на узле с определенным родителем. Узлы вставляются как соседние братья и сестры, как видно из следующего примера:
const parent = document.createElement('ul')
const child = document.createElement('li')
child.append('First item')
parent.append(child)
child.after(document.createElement('li'))
parent.outerHTML
// <ul><li>First item</li><li></li></ul>
Возвращаемое значение не undefined
после () .
4. до ()
Метод before похож на after()
const parent = document.createElement('ul')
const child = document.createElement('li')
child.append('First item')
parent.append(child)
const child1 = document.createElement('li')
child1.append('Second item')
const child2 = document.createElement('li')
child2.append('Third item')
child.before(child1, child2)
parent.outerHTML
/* Outputs:
<ul>
<li>Second item</li>
<li>Third item</li>
<li>First item</li>
</ul>
*/
Еще раз, возвращаемое значение не undefined.
Соответствующий метод jQuery — before () .
5. replaceWith ()
Предположим, мы хотели заменить один узел DOM другим. Конечно, они могут иметь детей, поэтому эта операция заменит целые поддеревья DOM. Перед введением этого набора удобных методов мы бы использовали replaceChild () :
const parent = document.createElement('ul')
parent.innerHTML = `
<li>first</li>
<li>second</li>
<li>third</li>
`
parent.outerHTML
// <ul><li>first</li><li>second</li><li>third</li></ul>"
const secondChild = parent.children[1]
const newSecondChild = document.createElement('li')
newSecondChild.innerHTML = '<a href="#">second</a>'
secondChild.parentNode.replaceChild(newSecondChild, secondChild)
parent.outerHTML
/* Outputs:
<ul>
<li>first</li>
<li><a href="#">second</a></li>
<li>third</li>
</ul>
*/
( innerHTML и литералы шаблона были использованы для облегчения построения дерева )
Эту же операцию можно выполнить с replaceWith гораздо менее многословно:
parent = document.createElement('ul')
parent.innerHTML = `
<li>first</li>
<li>second</li>
<li>third</li>
`
secondChild = parent.children[1]
newSecondChild = document.createElement('li')
newSecondChild.innerHTML = '<a href="#">second</a>'
secondChild.replaceWith(newSecondChild)
Помимо более короткого синтаксиса, особенностью этого более нового метода является то, что он принимает несколько аргументов, позволяя заменить узел списком других узлов. Продолжая предыдущий интерактивный сеанс JavaScript:
const newerSecondChild = document.createElement('li')
newerSecondChild.append('another item')
const newThirdChild = document.createElement('li')
newThirdChild.append('yet another item')
newSecondChild.replaceWith(newerSecondChild, newThirdChild)
parent.outerHTML
/* Outputs:
<ul>
<li>first</li>
<li>another item</li>
<li>yet another item</li>
<li>third</li>
</ul>
*/
Здесь также возвращаемое значение метода не undefined
Вы можете сравнить это с методом омонима jQuery .
удалять()
Как насчет удаления узлов из дерева DOM? «Старый» метод — это removeChild () . Как указано его именем, он должен быть вызван на родительском узле n
n.parentNode.removeChild(n)
Тем не менее, с помощью remove () эта операция значительно упрощается:
const parent = document.createElement('ul')
const n = document.createElement('li')
parent.append(n)
parent.outerHTML
// <ul><li></li></ul>
n.remove()
parent.outerHTML
// <ul></ul>
Разница с аналоговой операцией в jQuery заключается в том, как обрабатываются прослушиватели событий, подключенные к удаленному узлу. jQuery удаляет все связанные события и данные, связанные с элементом, в то время как нативный метод не касается прослушивателей событий:
const parent = document.createElement('ul')
const n = document.createElement('li')
parent.append(n)
n.addEventListener('test', console.log.bind(console))
const e = new Event('test')
n.dispatchEvent(e)
Event {isTrusted: false, type: "test", ...
n.remove()
n.dispatchEvent(e)
Event {isTrusted: false, type: "test", ...
Это поведение больше похоже на метод отсоединения jQuery.
Поддержка браузера
На момент написания статьи поддержка первых пяти вспомогательных методов — prepend()
append()
before()
after()
replaceWith()
- Chrome реализует их , начиная с версии 54.
- Firefox поддерживает их , начиная с версии 49.
- Safari поддерживает их , начиная с версии 10.
- Opera поддерживает их, начиная с версии 41.
- К сожалению, они не поддерживаются ни в Internet Explorer, ни в Microsoft Edge (хотя для Edge эта функция находится в разработке .
Метод remove
более широкой поддержкой . Microsoft Edge реализует его начиная с версии 14.
Для тех браузеров, которые еще не предоставляют эти методы, доступно несколько полифильмов. childNode.js является одним из них, и другие полифилы можно найти на страницах MDN, посвященных этим методам, которые уже упоминались в этой статье.
7. Бонусный метод: insertAdjacentHTML
В заключение несколько слов о insertAdjacentHTML . Он обеспечивает операции вставки, аналогичные первым четырем перечисленным методам — append()
prepend()
after()
before()
const parent = document.createElement('div')
parent.insertAdjacentHTML('beforeend', '<p>A paragraph</p>')
parent.insertAdjacentHTML('beforeend', '<p>Another paragraph</p>')
parent.insertAdjacentHTML('afterbegin', '<p>Yet another paragraph</p>')
const grandParent = document.createElement('div')
grandParent.append(parent)
parent.insertAdjacentHTML('afterend', '<div class="after"></div>')
parent.insertAdjacentHTML('beforebegin', '<div class="before"></div><div class="before2"></div>')
grandParent.outerHTML
/* Outputs:
<div>
<div class="before"></div>
<div class="before2"></div>
<div>
<p>Yet another paragraph</p>
<p>A paragraph</p>
<p>Another paragraph</p>
</div>
<div class="after"></div>
</div>
*/
Обратите внимание, как мы должны были сделать parent
beforebegin
afterend
К счастью, insertAdjacentHTML()
доступен везде .
Вывод
И теперь мы в конце этого краткого обзора этих методов DOM, вдохновленных jQuery. Я надеюсь, что в ходе этой статьи я продемонстрировал, как продвигается нативный API DOM и как эти нативные методы часто могут просто заменить свои аналоги jQuery.
Но что вы думаете? Это как-то нарушает вашу зависимость от jQuery? Или отсутствие поддержки IE является нарушителем соглашения? Я хотел бы услышать от вас в комментариях ниже.
Эта статья была рецензирована Себастьяном Зейтцем . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!