- PVSM.RU - https://www.pvsm.ru -
* Это шутка.

(картинка позаимствована где-то в интернете)
[ Репозиторий [1] ]
Всем привет.
Уже давно прошли времена обязательной поддержки 6, 7, 8 Ослов и неизбежного использования jQuery, DOM API постепенно приводится к единому виду, но я всё так же часто встречаю на просторах интернета утверждения о том, что VanillaJS — это длинная колбаса.
Мол, зачем мне писать вот так:
document.querySelector('.selector');
Если я могу написать вот так:
$('.selector');
Я отчасти согласен с этим, потому что, периодически, необходимо выбирать элементы много раз в приложении. Приходится определять функции, делающую выборку, прямо в коде:
// selects one node matched given selector
function $(selector, ctx) {
return (ctx || document).querySelector(selector);
}
// selects all nodes matched given selector
function $$(selector, ctx) {
return [].slice.call((ctx || document).querySelectorAll(selector));
}
Это касается крошечных скриптов. Для крупных проектов, конечно, нужно юзать какой-нибудь фреймворк, который берет на себя все манипуляции над DOM.
Но я совершенно несогласен с тем, что для обычной выборки нужно подклюать что-то большое (Zepto, jQuery).
«Движение» против использования jQuery и за использование нативных методов DOM существует уже несколько лет. Вспомним два самых известных сайта youmightnotneedjquery [2] и vanilla-js [3]. Оба сразу отталкивают новичка устаревшими альтернативами. vanilla-js показывает ужасные примеры использования AJAX и анимаций, второй грешит только беспощадным XMLHttpRequest. Оба сайта ни слова не говорят о Fetch API.
Хотя, если присмотреться, и с анимациями у второго не всё гладко. В качестве альтернативы они предлагают воспользоваться transition, хотя CSS Animations существуют давно и, самое главное, Web Animations JavaScript API [4] уже имплементирован в Хроме и Файерфоксе и неплохо полифилится для других браузеров.
***
Для того, чтоб получить небольшую DOM библиотеку с минимальным необходимым набором методов, я когда-то сделал библиотеку, с шуточным названием balalaika.js [5]. Напомню, балалайка — jQuery-подобная микробиблиотека, с очень небольшим набором методов: on, off, is, extend.
Но и она устарела. Метод is потерял свою актуальность, так как метод matches [6] стал кроссбраузерным. extend самоуничтожился, так как в JavaScript пришел Object.assign [7], on и off просто-напросто не нужны, по причине, озвученной выше: фреймворки.
Я решил немного обновить эту библиотеку, выпилив все методы и оставив только функциональность, отвечающу за выборку элементов. Так как это изменение полностью ломает совместимость с балалайкой, было решено вынести её в отдельный проект с другим названием «bala» — обрубленное старое название (как и библиотека), — «пуля» по-испански.
Кроме всего прочего, целью bala.js (приписывать к названию букву «е» строго запрещается, по крайней мере, при детях) является улучшение того, что сейчас иногда называют «плагинами к VanillaJS». Я очень люблю библиотеки без зависимостей, но почти все они предлагают инициализировать скрипт с передачей DOM Element.
myAwesomeLib(document.getElementByID('block'));
В таких случаях мне бы хотелось иметь больше возможностей: передать селектор, передать NodeList или, на худой конец, инстанс jQuery. Подключив к такому инструменту ~400 символов кода, инициализация скрипта будет более гибкой.
Крайне часто, при выборке, требуется только одна нода (например, для вызова appendChild). Но каждый раз запрашивать нулевой элемент выборки никому не нравится.
$('.selector')[0].appendChild(node);
Поэтому, была добавлена симпатичная альтернатива в виде статичного метода $.one, который работает в точности так же, как и основная функция, но возвращает нулевой элемент или null
// всего одним символом больше, а выглядит на тысячу символов лучше
$.one('.selector').appendChild(node);
$.one, кроме всего, служит двум целям: не создавать дополнительную переменную (в таких библиотеках их обычно две: $$ и $) и оставить возможность симпатичного импорта.
import $ from 'bala';
var $ = require('bala');
Возможность передать в функцию всё, что угодно: селектор, узел, массив, NodeList, jQuery и любой другой array-like объект
$('.one, two')
$(document.querySelectorAll('.selector'));
$(document.body);
$(element.children);
$(jQuery('.selector'));
$([document.querySelector('.one'), document.querySelector('.two')])
Поиск элемента в другом элементе
var elements = $('.my-selector', element);
DOM ready
$(function() {
alert('DOM is ready');
});
Не забывайте, что вместо использования DOM ready можно просто указать скрипты в конце тега body
...
<script src="app.js"></script>
</body>
</html>
Парсинг
var div = $('<div><span class="yeah"></span></div>');
Парсинг контекстных элементов
Для персинга элементов, требующих контекст, нужно передать имя тега-родителя (он создается динамически), например, для парсинга td нужно передать tr, для парсинга tr нужно передать tbody, для парсинга option нужно передать select.
var cells = $('<td>foo</td><td>bar</td>', 'tr');
Плагины
Расширять bala.js можнно как и любую другую jQuery-подобную библиотеку.
$.fn.toggle = function(boolean) {
this.forEach(function(item) {
item.hidden = boolean;
});
};
$('.button').toggle(false); // hides all buttons
Как глобальную переменную на странице
<script>
$=function(d,e,c,f,g){c=function(a,b){return new f(a,b)};f=function(a,b){e.push.apply(this,a?a.nodeType||a==window?[a]:""+a===a?/</.test(a)?((g=d.createElement(b||"q")).innerHTML=a,g.children):(b&&c(b)[0]||d).querySelectorAll(a):/f/.test(typeof a)?/c/.test(d.readyState)?a():d.addEventListener("DOMContentLoaded",a):a:e)};c.fn=f.prototype=e;c.one=function(a,b){return c(a,b)[0]||null};return c}(document,[]);
</script>
<script>
$(function() {
alert($('.my-selector').length);
});
</script>
Как локальную переменную в IIFE
(function(win, $) {
// your code starts here
$(function() {
alert($('.my-selector').length);
});
// your code ends here
})(window, function(d,e,c,f,g){c=function(a,b){return new f(a,b)};f=function(a,b){e.push.apply(this,a?a.nodeType||a==window?[a]:""+a===a?/</.test(a)?((g=d.createElement(b||"q")).innerHTML=a,g.children):(b&&c(b)[0]||d).querySelectorAll(a):/f/.test(typeof a)?/c/.test(d.readyState)?a():d.addEventListener("DOMContentLoaded",a):a:e)};c.fn=f.prototype=e;c.one=function(a,b){return c(a,b)[0]||null};return c}(document,[]));
Можно установить bala.js с помощью NPM
npm install --save balajs
Перебор
True-way
for(let element of $('.button')) {
console.log(element);
}
или old-school-way
$('.button').forEach(function(element) {
console.log(element);
});
Добавление стилей
$('.my-selector').forEach(function(element) {
element.style.color = 'red';
});
или, если нужно изменить стиль только для одного элемента:
$.one('.my-selector').style.color = 'red';
Делегирование событий
С методом closest не нужно использовать страшный while.
$('.my-selector').forEach(function(element) {
element.addEventListener('click', function(evt) {
if(this.contains(evt.target.closest('.delegated-selector'))) {
alert('yep!');
}
});
});
или так
$.one('.my-selector').addEventListener('click', function(evt) {
if(this.contains(evt.target.closest('.delegated-selector'))) {
alert('yep!');
}
});
Удаление элементов
$('.my-selector').forEach(function(element) {
element.remove();
});
Удаление одного элемента
$.one('.my-selector').remove();
Вам может понадобиться полифил DOM4 [8] для методов element.remove и element.closest.
(Эти примеры, конечно же, не являются заслугой bala.js. Это заслуга разработчиков браузеров и спецификаций.)
Вам всё еще нужен jQuery?
Если да, ниже еще два примера.
Анимации
С помощью Web Aimations API можно прописывать анимации прямо в JS коде (они будут многократно быстрее, чем таковые из jQuery, благодаря GPU ускорению).
// код позаимствован из гугловской статьи
$.one('.my-selector').animate([
{transform: 'translate(' + snowLeft + 'px, -100%)'},
{transform: 'translate(' + snowLeft + 'px, ' + window.innerHeight + 'px)'}
], {
duration: 1500,
iterations: 10,
delay: 300
});
Само собой, это работает и без всяких библиотек (не счтиая того, что вам может понадобиться полифилл Web Animations API [9]).
Ajax
А вот пример обращения к серверу. Fetch API элегантно заменяет устрашающий XMLHttpRequest.
fetch('user.json')
.then(function(response) {
return response.json();
})
.then(function(user) {
console.log(user);
})
.catch(alert);
Вам может понадобиться полифил Fetch API [10].
Всё еще хотите подключить jQuery? Серьезно?
Да, для поддержки старых Internet Explorer и Webkit-based мобильных браузеров вам придется подключить полифилы. Но не спешите кидаться какашками и вспомните, что Internet Explorer, с недавних пор, ведет себя хорошо, имплементируя самые невероятные фичи раньше, чем это делают люди занимающиеся Blink и V8, обещая в скором времени отказаться от поддержки всех версий до 11; люди покупают новые айфоны с обновленным сафари…
А еще, предлагаю ознакомиться с этим сервисом: polyfill.io [11]. Ребята полифилят фичи глядя на User-Agent посетителя страницы. Спасибо chico [12] и RReverser [13] за находку.
Через год-два в этих полифилах не будет нужды, но появятся новые, и ваш покорный слуга, возможно, снова напишет пост и предложит вам отказаться от jQuery и расскажет о библиотеке ba.js.
Автор: Finom
Источник [14]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/107035
Ссылки в тексте:
[1] Репозиторий: https://github.com/finom/bala
[2] youmightnotneedjquery: http://youmightnotneedjquery.com/
[3] vanilla-js: http://vanilla-js.com/
[4] Web Animations JavaScript API: https://developers.google.com/web/updates/2014/05/Web-Animations-element.animate-is-now-in-Chrome-36
[5] balalaika.js: https://github.com/finom/balalaika
[6] matches: https://developer.mozilla.org/ru/docs/Web/API/Element/matches
[7] Object.assign: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
[8] полифил DOM4: https://github.com/WebReflection/dom4
[9] полифилл Web Animations API: https://github.com/web-animations/web-animations-js
[10] полифил Fetch API: https://github.com/github/fetch
[11] polyfill.io: https://cdn.polyfill.io/v2/docs/
[12] chico: http://habrahabr.ru/users/chico/
[13] RReverser: http://habrahabr.ru/users/rreverser/
[14] Источник: http://habrahabr.ru/post/273751/
Нажмите здесь для печати.