Статьи

Lose jQuery Bloat — манипуляция DOM с NodeList.js

В последние годы jQuery стал де-факто библиотекой JavaScript в Интернете. Он устраняет многие несоответствия между браузерами и добавляет желаемый слой синтаксического сахара в сценарии на стороне клиента. Одной из основных проблем, которую он отвлекает, является манипулирование DOM, но с момента его создания собственные API-интерфейсы для браузеров значительно улучшились, и идея о том, что вам может не понадобиться jQuery , стала набирать популярность.

Вот несколько причин, почему:

  1. JQuery включает в себя множество функций, которые вам не нужны или не используются (поэтому вес не требуется).
  2. JQuery это слишком много вещей, чтобы слишком много людей. Часто небольшие библиотеки могут лучше выполнять определенные задачи.
  3. С точки зрения манипулирования DOM, браузерные API теперь могут делать большую часть того, что может делать jQuery.
  4. API браузеров теперь более синхронизированы, например, используя addEventListener вместо attatchEvent .

Так в чем проблема?

Проблема в том, что манипулирование DOM с использованием ванильного (или простого) JavaScript может быть проблемой по сравнению с jQuery. Это потому, что вам нужно читать и писать более избыточный код и иметь дело с бесполезным NodeList браузера .

Сначала давайте посмотрим, что такое NodeList в соответствии с MDN:

Объекты NodeList являются коллекциями узлов, таких как возвращаемые Node.childNodes и методом document.querySelectorAll.

И иногда есть живые списки NodeLists (что может сбить с толку):

В некоторых случаях NodeList является живой коллекцией, что означает, что изменения в DOM отражаются в коллекции. Например, Node.childNodes является живым.

Это может быть проблемой, потому что вы не можете сказать, какие из них являются живыми, а какие статичными. Если вы не удалите каждый из узлов из NodeList а затем убедитесь, что NodeList пуст. Если он пуст, значит, у вас есть живой NodeList (что просто плохая идея).

Также браузер не предоставляет никаких полезных методов для манипулирования этими объектами NodeList .

Например, к сожалению, невозможно forEach узлы с помощью forEach :

 var nodes = document.querySelectorAll('div'); nodes.forEach(function(node) { // do something }); // Error: nodes.forEach is not a function 

Итак, вы должны сделать:

 var nodes = document.querySelectorAll('div'); for(var i = 0, l = nodes.length; i < l; i++) { var node = nodes[i]; // do something } 

Или даже остались с использованием «взломать»:

 [].forEach.call(document.querySelectorAll('div'), function(node) { // do something }); 

Родной NodeList браузера NodeList только один метод: item . Это возвращает узел из NodeList по индексу. Это совершенно бесполезно, когда мы можем получить доступ к этому узлу, как если бы мы использовали массив (используя array[index] ):

 var nodes = document.querySelectorAll('div'); nodes.item(0) === nodes[0]; // true 

Вот тут- то и появляется NodeList.js — чтобы упростить манипулирование DOM с помощью нативных API браузера, как и с jQuery, но с минимальными 4k.

Решение

Я создал NodeList.js, потому что я всегда использовал нативные API-интерфейсы DOM, но хотел сделать их более краткими, чтобы убрать большую часть избыточности при написании моего кода (например, for циклов).

NodeList.js — это оболочка для нативных API DOM, которая позволяет вам манипулировать массивом узлов (AKA my NodeList ), как если бы это был один узел. Это дает вам гораздо больше функциональности, чем родные объекты браузера NodeList .

Если это звучит хорошо для вас, возьмите копию NodeList.js из официального репозитория GitHub и следуйте инструкциям этого руководства.

Использование:

Выбор узлов DOM прост:

 $$(selector); // returns my NodeList 

Этот метод использует querySelectorAll(selector) под капотом.

Но как это сочетается с JQuery?

Рад, что ты спросил. Давайте поместим ванильный JS, jQuery и NodeList.js один на один.

Допустим, у нас есть три кнопки:

 <button></button> <button></button> <button></button> 

Давайте изменим текст каждой кнопки на «Click Me» :

Ваниль JS:

 var buttons = document.querySelectorAll('button'); // returns browser's useless NodeList for(var i = 0, l = buttons.length; i < l; i++) { buttons[i].textContent = 'Click Me'; } 

JQuery:

 $('button').text('Click Me'); 

NodeList.js:

 $$('button').textContent = 'Click Me'; 

Здесь мы видим, что NodeList.js может эффективно обрабатывать NodeList как один узел. То есть у нас есть ссылка на NodeList и мы просто устанавливаем его свойство textContent «Click Me» . Затем NodeList.js сделает это для каждого узла в NodeList . Аккуратно, а?

Если бы мы хотели цепочку методов (а-ля jQuery), мы бы сделали следующее, что возвращает ссылку на NodeList :

 $$('button').set('textContent', 'Click Me'); 

Теперь давайте добавим слушатель события click для каждой кнопки:

Ваниль JS:

 var buttons = document.querySelectorAll('button'); // returns browser's useless NodeList for(var i = 0, l = buttons.length; i < l; i++) { buttons[i].addEventListener('click', function() { this.classList.add('clicked'); }); } 

JQuery:

 $('button').on('click', function() { $(this).addClass('click'); // or mix jQuery with native using `classList`: this.classList.add('clicked'); }); 

NodeList.js:

 $$('button').addEventListener('click', function() { this.classList.add('clicked'); }); 

Итак, метод jQuery on довольно хорош. Моя библиотека использует Native DOM API браузера (следовательно, addEventListener ), но это не мешает нам создавать псевдоним для метода:

 $$.NL.on = $$.NL.addEventListener; $$('button').on('click', function() { this.classList.add('clicked'); }); 

Ницца! И это демонстрирует, как именно мы добавили бы наши собственные методы:

 $$.NL.myNewMethod = function() { // loop through each node with a for loop or use forEach: this.forEach(function(element, index, nodeList) {...} // where `this` is the NodeList being manipulated } 

NodeList.js о методах массива

NodeList.js наследуется от Array.prototype , но не напрямую, так как некоторые методы изменены, поэтому имеет смысл использовать их с NodeList (массивом узлов).

Push и Unshift

Например: методы push и unshift могут принимать узлы только в качестве аргументов, иначе они выдадут ошибку:

 var nodes = $$('body'); nodes.push(document.documentElement); nodes.push(1); // Uncaught Error: Passed arguments must be a Node 

Таким образом, push и unshift возвращают NodeList чтобы разрешить цепочку методов, то есть это не то же самое, что Array#unshift методы JavaScript Array#push или Array#unshift , которые принимают все и возвращают новую длину Array . Если нам нужна длина NodeList мы просто используем свойство length .

Оба эти метода, так же как и встроенные в JavaScript методы Array , изменяют NodeList .

Concat

Метод concat будет принимать следующие аргументы:

  • Node
  • NodeList (как родной браузер, так и версия NodeList.js)
  • HTMLCollection
  • Array of Nodes
  • Array of NodeList
  • Array of HTMLCollection

concatрекурсивный метод , поэтому эти массивы могут быть настолько глубокими, насколько нам нравится, и будут сглажены. Однако если какой-либо из элементов в переданных массивах не имеет Node , NodeList или HTMLCollection он выдаст Error .

concat возвращает новый NodeList точно так же, как и метод javascript Array#concat .

Pop, Shift, Map, Slice, Filter

Методы pop и shift могут принимать необязательный аргумент относительно количества узлов, которые нужно NodeList или shift из NodeList . В отличие от встроенного в JavaScript Array#pop или Array#shift котором всегда появляется или shift один элемент из массива независимо от того, что передается в качестве аргумента.

Метод map возвращает NodeList если каждое сопоставленное значение является Node , или массив сопоставленных значений, если нет.

Методы слайса и фильтра действуют так же, как и в реальных массивах, но возвращают NodeList .

Поскольку NodeList.js не наследуется напрямую от Array.prototype если метод добавляется в Array.prototype после загрузки NodeList.js, он не будет наследоваться.

Вы можете проверить остальные методы массива NodeList.js здесь .

Специальные методы

Существует четыре метода, уникальных для NodeList.js, а также свойство под названием owner , которое является эквивалентом свойства prevObject в jQuery.

Методы get и set :

Есть некоторые элементы со свойствами, уникальными для этого вида элементов (например, свойство href в теге привязки). Вот почему $$('a').href вернет undefined — это свойство, которое наследует не каждый элемент в NodeList . Вот как мы будем использовать метод get для доступа к этим свойствам:

 $$('a').get('href'); // returns array of href values 

Метод set можно использовать для установки этих свойств для каждого элемента:

 $$('a').set('href', 'https://sitepoint.com/'); 

set также возвращает NodeList чтобы разрешить цепочку методов. Мы можем использовать это для таких вещей, как textContent (оба эквивалентны):

 $$('button').textContent = 'Click Me'; $$('button').set('textContent', 'Click Me'); // returns NodeList so you can method chain 

Мы также можем установить несколько свойств за один вызов:

 $$('button').set({ textContent: 'Click Me', onclick: function() {...} }); 

И все вышеперечисленное может быть сделано с произвольными свойствами, такими как style :

 $$('button').style; // this returns an `Array` of `CSSStyleDeclaration` $$('button').style.set('color', 'white'); $$('button').style.set({ color: 'white', background: 'lightblue' }); 

Метод call

Метод call позволяет вам вызывать эти методы, уникальные для элемента (например, pause для элемента video):

 $$('video').call('pause'); // returns NodeList back to allow Method Chaining 

Метод item

Метод item является эквивалентом метода eQ в jQuery . Он возвращает NodeList содержащий только узел переданного индекса:

 $$('button').item(1); // returns NodeList containing the single Node at index 1 

owner недвижимости

Свойство владельца является эквивалентом prevObject jQuery.

 var btns = $$('button'); btns.style.owner === btns; // true 

btns.style возвращает массив стилей, а owner возвращает вам NodeList которого был отображен style .

NodeList.js Совместимость

Моя библиотека совместима со всеми основными новыми браузерами, как описано ниже.

браузер Версия
Fire Fox 6+
Сафари 5.0.5+
Хром 6+
IE 9+
опера 11.6+

Вывод

Теперь мы наконец можем работать с полезным объектом NodeList !

Приблизительно за 4k вы получаете все функции, упомянутые выше, и еще много, о чем вы можете узнать в репозитории GitHub NodeList.js .

Поскольку NodeList.js использует браузер в качестве зависимости, никаких обновлений делать не нужно. Всякий раз, когда браузеры добавляют новые методы / свойства к элементам DOM, вы автоматически сможете использовать эти методы / свойства через NodeList.js. Все это означает, что единственное, о чем вам нужно беспокоиться, — это методы, от которых избавляются браузеры. Обычно они используются очень редко, потому что мы не можем сломать сеть.

Так что ты думаешь? Это библиотека, которую вы хотели бы использовать? Какие-то важные функции отсутствуют? Я хотел бы услышать от вас в комментариях ниже.