Нравится нам это или нет, все больше и больше разработчиков знакомятся с миром JavaScript через jQuery. Во многом эти новички счастливчики. У них есть доступ к множеству новых API-интерфейсов JavaScript, которые значительно облегчают процесс обхода DOM (то, от чего многие люди зависят от jQuery). К сожалению, они не знают об этих API!
В этой статье мы рассмотрим различные распространенные задачи jQuery и преобразуем их как в современный, так и в устаревший JavaScript.
Modern vs. Legacy — Для каждого элемента в списке ниже вы найдете современный, «крутой ребенок» способ решения задачи и унаследованную версию «сделайте старые браузеры счастливыми». Выбор, который вы выберете для своих проектов, будет во многом зависеть от ваших посетителей.
Прежде чем мы начнем
Обратите внимание, что некоторые из унаследованных примеров в этой статье будут использовать простую кросс-браузерную функцию addEvent
. Эта функция просто обеспечит нормализацию модели событий, рекомендованной W3C, addEventListener
и устаревшего attachEvent
Internet Explorer.
Поэтому, когда я ссылаюсь на addEvent(els, event, handler)
в приведенных ниже фрагментах устаревшего кода, упоминается следующая функция.
var addEvent = (function () { var filter = function (el, type, fn) { для (var i = 0, len = el.length; i <len; i ++) { addEvent (el [i], type, fn); } }; if (document.addEventListener) { функция возврата (el, type, fn) { if (el && el.nodeName || el === окно) { el.addEventListener (type, fn, false); } else if (el && el.length) { фильтр (эл, тип, фн); } }; } функция возврата (el, type, fn) { if (el && el.nodeName || el === окно) { el.attachEvent ('on' + type, function () {return fn.call (el, window.event);}); } else if (el && el.length) { фильтр (эл, тип, фн); } }; }) (); // использование addEvent (document.getElementsByTagName ('a'), 'click', fn);
1 — $('#container');
Этот вызов функции запросит DOM для элемента с id
container
и создаст новый объект jQuery
.
Современный JavaScript
var container = document.querySelector ('# container');
querySelector
является частью API селекторов , который предоставляет нам возможность запрашивать DOM, используя CSS-селекторы, с которыми мы уже знакомы.
Этот конкретный метод вернет первый элемент, который соответствует переданному селектору.
наследие
var container = document.getElementById ('container');
Обратите особое внимание на то, как вы ссылаетесь на элемент. При использовании getElementById
вы передаете только одно значение, в то время как с querySelector
ожидается селектор CSS.
2 — $('#container').find('li');
На этот раз мы не ищем ни одного элемента; вместо этого мы собираем любое количество элементов списка, которые являются потомками #container
.
Современный JavaScript
var lis = document.querySelectorAll ('# container li');
querySelectorAll
вернет все элементы, которые соответствуют указанному селектору CSS.
Ограничения селектора
Хотя почти все соответствующие браузеры поддерживают API селекторов, определенные селекторы CSS, которые вы передаете, по-прежнему ограничены возможностями браузера. Перевод: Internet Explorer 8 будет поддерживать только селекторы CSS 2.1.
наследие
var lis = document.getElementById ('container'). getElementsByTagName ('li');
3 — $('a').on('click', fn);
В этом примере мы прикрепляем слушатель события click
ко всем тегам привязки на странице.
Современный JavaScript
[] .forEach.call (document.querySelectorAll ('a'), function (el) { el.addEventListener ('click', function () { // якорь был нажат }, ложный); });
Вышеуказанный фрагмент выглядит страшно, но не так уж плохо. Поскольку querySelectorAll
возвращает статический NodeList
а не Array
, мы не можем напрямую обращаться к методам, таким как forEach
. Это исправляется путем вызова forEach
для объекта Array
и передачи результатов querySelectorAll
следующим this
.
наследие
var anchors = document.getElementsbyTagName ('a'); addEvent (привязки, «щелчок», fn);
4 — $('ul').on('click', 'a', fn);
Ааа — этот пример немного отличается. На этот раз фрагмент jQuery использует делегирование событий. click
щелчков применяется ко всем неупорядоченным спискам, однако функция обратного вызова будет срабатывать только в том случае, если цель (на которую специально щелкнул пользователь) является привязкой.
Современный JavaScript
document.addEventListener ('click', function (e) { if (e.target.matchesSelector ('ul a')) { // продолжить } }, ложный);
Технически этот ванильный метод JavaScript отличается от примера jQuery. Вместо этого он прикрепляет прослушиватель событий непосредственно к document
. Затем он использует новый метод matchesSelector
чтобы определить, соответствует ли target
— узел, на который щелкнули, — предоставленный селектор. Таким образом, мы подключаем одного слушателя событий, а не многих.
Обратите внимание, что на момент написания этой статьи все браузеры реализовывали matchesSelector
через свои собственные соответствующие префиксы: mozMatchesSelector
, webkitMatchesSelector
и т. Д. Чтобы нормализовать метод, можно написать:
вар совпадений; (функция (док) { спички = doc.matchesSelector || doc.webkitMatchesSelector || doc.mozMatchesSelector || doc.oMatchesSelector || doc.msMatchesSelector; }) (Document.documentElement); document.addEventListener ('click', function (e) { if (match.call (e.target, 'ul a')) { // продолжить } }, ложный);
При использовании этого метода в Webkit совпадения будут ссылаться на
webkitMatchesSelector
, а в Mozilla —mozMatchesSelector
.
наследие
var uls = document.getElementsByTagName ('ul'); addEvent (uls, 'click', function () { var target = e.target || e.srcElement; if (target && target.nodeName === 'A') { // продолжить } });
В качестве запасного nodeName
мы определяем, nodeName
свойство nodeName
(имя целевого элемента) нашему желаемому запросу. Обратите особое внимание на тот факт, что старые версии Internet Explorer иногда играют по своим собственным правилам — как ребенок, который ест ланч во время обеда. Вы не сможете получить доступ к target
напрямую из объекта event
. Вместо этого вам нужно искать event.srcElement
.
5 — $('#box').addClass('wrap');
jQuery предоставляет полезный API для изменения имен классов на множестве элементов.
Современный JavaScript
document.querySelector ( '# окно') classList.add ( 'обертка').
Этот новый метод использует новый API classList
для add
, remove
и toggle
имен классов.
var container = document.querySelector ('# box'); container.classList.add ( 'обертка'); container.classList.remove ( 'обертка'); container.classList.toggle ( 'обертка');
наследие
var box = document.getElementById ('box'), hasClass = function (el, cl) { var regex = new RegExp ('(?: \\ s | ^)' + cl + '(?: \\ s | $)'); return !! el.className.match (regex); }, addClass = function (el, cl) { el.className + = '' + cl; }, removeClass = function (el, cl) { var regex = new RegExp ('(?: \\ s | ^)' + cl + '(?: \\ s | $)'); el.className = el.className.replace (regex, ''); }, toggleClass = function (el, cl) { hasClass (el, cl)? removeClass (el, cl): addClass (el, cl); }; addClass (box, 'drago'); removeClass (box, 'drago'); toggleClass (box, 'drago'); // если элемент не имеет класса drago, добавьте его.
Аварийная техника требует чуть больше работы, а?
6 — $('#list').next();
next
метод jQuery вернет элемент, который следует сразу за текущим элементом в упакованном наборе.
Современный JavaScript
var next = document.querySelector ('# list'). nextElementSibling; // IE9
nextElementSibling
будет относиться конкретно к следующему узлу элемента , а не к любому узлу (текст, комментарий, элемент). К сожалению, Internet Explorer 8 и ниже не поддерживают его.
наследие
var list = document.getElementById ('list'), next = list.nextSibling; // мы хотим, чтобы следующий элемент узла ... не текст. while (next.nodeType> 1) next = next.nextSibling;
Есть несколько способов написать это. В этом примере мы обнаруживаем nodeType
узла, который следует за указанным элементом. Это может быть текст, элемент или даже комментарий. Поскольку нам конкретно нужен следующий элемент, мы хотим, чтобы nodeType
1
. Если next.nodeType
возвращает число больше 1
, мы должны пропустить его и продолжать, так как это, вероятно, текстовый узел.
7 — $('<div id=box></div>').appendTo('body');
Помимо запросов к DOM, jQuery также предлагает возможность создавать и вставлять элементы.
Современный JavaScript
var div = document.createElement ('div'); div.id = 'box'; document.body.appendChild (дела);
В этом примере нет ничего современного; это то, как мы завершили процесс создания и внедрения элементов в DOM в течение долгого времени.
Скорее всего, вам понадобится добавить контент к элементу, и в этом случае вы можете использовать innerHTML
или createTextNode
.
div.appendChild (document.createTextNode ('wacka wacka')); // или div.innerHTML = 'wacka wacka';
8 — $(document).ready(fn)
Метод document.ready
jQuery невероятно удобен. Это позволяет нам начать выполнение кода как можно скорее после загрузки DOM.
Современный JavaScript
document.addEventListener ('DOMContentLoaded', function () { // веселиться });
Стандартизированный как часть HTML5, событие DOMContentLoaded
будет DOMContentLoaded
, как только документ будет завершен при анализе.
наследие
// http://dustindiaz.com/smallest-domready-ever функция готова (cb) { /in/.test(document.readyState) // in = loadINg ? setTimeout ('ready (' + cb + ')', 9) : cb (); } готов (функция () { // взять что-то из DOM });
Резервное решение, каждые девять миллисекунд, обнаружит значение document.readyState
. Если возвращается «загрузка», документ еще не был полностью проанализирован ( /in/.test()
. Однако после того, как document.readyState
будет равен «завершен», в этот момент выполняется функция обратного вызова пользователя.
9 — $('.box').css('color', 'red');
Если возможно, всегда добавляйте class
к элементу, когда вам нужно обеспечить особый стиль. Однако иногда стиль будет определяться динамически, и в этом случае его необходимо вставить как атрибут.
Современный JavaScript
[] .forEach.call (document.querySelectorAll ('. box'), function (el) { el.style.color = 'red'; // или добавить класс });
Еще раз, мы используем метод [].forEach.call()
, чтобы отфильтровать все элементы с помощью класса box
и сделать их красными через объект style
.
наследие
var box = document.getElementsByClassName ('box'), // см. пример № 10 ниже для кросс-браузерного решения я = box.length; while (i--> 0 && (box [i] .style.color = 'red'));
На этот раз мы немного усложняемся с циклом while. Да, это немного странно, не так ли? По сути, мы подражаем:
var i = 0, len; for (len = box.length; i <len; i ++) { box [i] .style.color = 'red'; }
Однако, поскольку нам нужно выполнить только одно действие, мы можем сохранить пару строк. Обратите внимание, что удобочитаемость гораздо важнее, чем сохранение двух строк — отсюда и моя «заразная» ссылка Тем не менее, всегда весело видеть, насколько сжато вы можете сделать свои петли. Мы разработчики; мы делаем такие вещи для удовольствия! Во всяком случае, не стесняйтесь придерживаться версии for
заявления.
10 — $()
Ясно, что мы не хотим копировать весь API jQuery. Как правило, для не-jQuery-проектов функция $
или $$
используется как сокращение для извлечения одного или нескольких элементов из DOM.
Современный JavaScript
var $ = function (el) { return document.querySelectorAll (el); }; // Usage = $ ('. Box');
Обратите внимание, что $
— это просто односимвольный указатель на document.querySelector
. Это экономит время!
наследие
if (! document.getElementsByClassName) { document.getElementsByClassName = function (cl, tag) { var els, match = [], я = 0, лен, regex = new RegExp ('(?: \\ s | ^)' + cl + '(?: \\ s | $)'); // Если имя тега не указано, // мы должны извлечь КАЖДЫЙ элемент из DOM els = document.getElementsByTagName (tag || "*"); if (! els [0]) возвращает false; for (len = els.length; i <len; i ++) { if (els [i] .className.match (regex)) { match.push (els [i]); } } возвращать спички; // массив элементов, которые имеют желаемое имя класса }; } // Очень простая реализация. Мы проверяем только идентификатор, класс или имя тега. // Не принимает CSS-селекторы в браузерах pre-querySelector. var $ = function (el, tag) { var firstChar = el.charAt (0); if (document.querySelectorAll) return document.querySelectorAll (el); switch (firstChar) { кейс "#": return document.getElementById (el.slice (1)); кейс ".": return document.getElementsByClassName (el.slice (1), tag); дефолт: return document.getElementsByTagName (el); } }; // Использование $ ( '# Контейнер'); $ ( 'Окно'); // любой элемент с классом box $ ('. box', 'div'); // ищем div с классом box $ ( 'Р'); // получить все элементы p
К сожалению, устаревший метод не так уж и минимален. Честно говоря, на данный момент, вы должны использовать библиотеку. jQuery высоко оптимизирован для работы с DOM, поэтому он так популярен! Приведенный выше пример, безусловно, будет работать, однако он не поддерживает сложные селекторы CSS в старых браузерах; эта задача немного сложнее!
Резюме
Для меня важно отметить, что я не призываю вас отказаться от jQuery. Я использую его почти во всех своих проектах. Тем не менее, не всегда будьте готовы принять абстракции, не потратив немного времени на изучение основного кода.
Я хотел бы, чтобы эта публикация служила живым документом, своего рода. Если у вас есть какие-либо свои собственные (или улучшения / разъяснения для моих примеров), оставьте комментарий ниже, и я буду время от времени обновлять эту публикацию новыми элементами. Добавьте эту страницу в закладки сейчас! Наконец, я хотел бы послать хет-чаевые этому набору примеров , которые послужили стимулом для этого поста.