Я знаю jQuery. Что дальше?

в 16:10, , рубрики: ajax, animation, css, dom, html5, javascript, jquery, transition, метки: , , , , , , ,

Перевод статьи от Реми Шарп (Remy Sharp)
Для любителей пролистывать в самый конец — уже накрыто.

Я завёл этот разговор: "Я знаю jQuery. Что теперь?" на jQuery UK 2013, но вместо моего обычного подхода пост-IT взрыва на моем столе, я написал сначала пост и создал по нему слайды. Так вот, мои (довольно неотредактированные) квази-прогулки о том, как я использовал jQuery и как я смотрю на то, где я использую нативные технологии браузера.
I know jQuery. Now what?

7 лет назад…

17 июня 2006 года я опубликовал мой самый первый пост в блоге: регулярный приём JavaScript и упрощение для синтаксиса jQuery. Он сокращал 14 строк JavaScript до 3 строк jQuery (до jQuery 1.0).

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

Я представил jQuery команде разработчиков, с которыми я работал еще с середины 2000-х годов, и даже дизайнеров он привлёк (так как они обычно понимают CSS селекторы (и с тех пор jQuery родился для дизайнеров)).

DOM навигации вдруг полегчало

Тогда DOM было трудно ориентироваться. Вы могли быть уверены, что, если он работает в Firefox 1.5, он не будет работать в IE6.

Простота, с которой можно было бы познать jQuery, привлекала меня. Вся DOM навигация была сделана с использованием CSS выражения, используя некую безумную коробку чёрной магии, ту, что придумал Джон Резиг — спасение моей ограниченной мощности мозга, и как только у меня были те узлы (nodes) DOM, я мог делать с ними то, что хотел (обычно это комбинации отображения, скрытия, исчезания и т.д.).

Понимание ajax

ajax

jQuery выделил для меня такую вещь как ajax. Термин ввели немногим ранее в 2005, но документация не была широкой, ни незначительной, обычной к пониманию (не забывайте о более низкой вычислительной мощности моего мозга).

Это был первый раз, когда мне реально нужно было иметь дело с объектом XMLHttpRequest, и видя его впервые, понимание события onreadystatechange и комбинации: this.status и this.readyState не было открыто ясным! jQuery (и другие библиотеки) также убрал беспорядок, который был с XHR в IE через ActiveX …

function getXmlHttpRequest() {
	var xhr;
	if (window.XMLHttpRequest) {
		xhr = new XMLHttpRequest();
	} else {
		try {
			xhr = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (e) {
			try {
				xhr = new ActiveXObject("Microsoft.XMLHTTP");
			} catch (e) {
				xhr = false;
			}
		}
	}
	return xhr;
}

// примечание: jQuery версия Джона гораздо элегантней!

Как только я увидел использование утилиты ajax внутри jQuery, чтобы поглощать URL HTML (обычный способ, который мы хотели, чтобы сделать ajax), то ajax вдруг щёлкнул.

jQuery быстро стал моей стандартной утилитой в течение многих лет. Это был мой швейцарский армейский нож, так сказать, название украл из заголовка разговора Адама!

Назад в будущее: сегодня

Назад в будущее

Давайте перематаем назад до сегодняшнего дня. Что произошло за эти годы?

Для новичков, по умолчанию моя начальная позиция не более, чем «включить jQuery по умолчанию». Я больше понимаю JavaScript и как он работает.

У меня есть свои критерии, когда включать jQuery и когда не включать jQuery. Но если я не включаю jQuery, то что?

За те 7 лет совсем мало что изменилось. Вероятно, одним из самых важных шагов стало введение querySelectorAll.

Будучи позволив дать нативной функции браузера выражения CSS и она сделает работу навигации по DOM — огромную (в буквальном смысле) часть jQuery. Базовая поддержка была в Chrome с самого начала, а в IE8 и Firefox 3.5 примерно в середине 2009 года.

У Эндрю Лунни (из PhoneGap и Adobe) есть сверкающая простота в блестящей функции:

var $ = document.querySelectorAll.bind(document);
Element.prototype.on = Element.prototype.addEventListener;

$('#somelink')[0].on('touchstart', handleTouch);

Потрясающе просто.

Я развил эту идею немного дальше и использовал её на нескольких специфичных проектах, добавив поддержку сцепления, циклов и упрощение синтаксиса. На выходе чуть меньше 200 байтов с gzip сжатием. Но дело в том, что сегодня нам доступны нативные функции, и я пытаюсь рассмотреть мою аудиторию, прежде чем подключать jQuery по умолчанию.

Когда я обычно использую jQuery

Прежде, чем я посмотрю на то, как я могу обойтись без jQuery, позвольте мне поделиться, когда я подключаю jQuery к проекту. Обычно это несколько весьма специфичных критериев, которые либо обязывают меня начинать с jQuery или переключиться с помощью заказного решения в пользу jQuery.

Прежде чем я что-то делаю, мне следует отметить, когда я абсолютно не использую jQuery: если я пытаюсь повторить ошибку браузера, я никогда не использую библиотеку. Если Вы пытаетесь загнать ошибку в угол, чтобы Вы могли определить проблему, Вам нужно, чтобы Ваш случай был минимально возможным (если, конечно, Вы не обнаруживаете ошибку в jQuery!).

1. Когда проект должен работать в не-добившихся-упеха браузерах

BBC были весьма шумными в том, что они определяли под "добившийся успеха" (примечание: ссылка работала до 08.05.2013, кэш гугла в помощь), и теперь я думаю об этом, это мой ориентир, когда подключать jQuery по умолчанию.

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

Что значит добиться успеха?

Это почти также просто, как: Есть ли в браузере querySelectorAll?

BBC использует следующий тест для достижения успеха:

if ('querySelector' in document && 
	'localStorage' in window && 
	'addEventListener' in window) {
	// ваше JavaScript приложение
}

Первое, что мне пришло в голову, IE8 не поддерживает addEventListener (но есть polyfill), так что если это важный браузер для проекта, то я знаю, что я не хочу начинать проект, прыгая через обруч ради IE8.

Это не значит, чтобы говорить о том, что те проекты, которые я начинаю без jQuery не будут поддерживать IE8, это большее, чем Вы хотели бы начать с простого, и держать развитие простых с самого начала. Если я начинаю проект с мешком хаков — я знаю, что будет стычка.

Я также классифицировал бы это как «когда сложность перевешивает лёгкость».

2. Когда это быстро и грязно

Quick'n Dirty

Если я создаю доказательство концепции, тестирование идеи и вообще хака, или создания JS «на потом», я обычно добавляю jQuery по умолчанию. Это спасает меня от раздумий.

jQuery-свобода

Вы можете подумать: «Использует ли Реми jQuery, и если нет, то он просто заново реализует всё это?».

Я, конечно, не хочу заново изобретать колесо. Если я замечу, что разработка без jQuery только и приводит меня к повтору большинства функциональности jQuery с нуля, то я напрасно трачу впустую своё собственное время.

Нет, есть много шаблонов, где я буду писать свой код приложения без библиотеки и полагаться на нативные технологии браузера, чтобы выполнить работу. И если небольшая часть той функциональности отсутствует, я мог бы обратиться к polyfill — но только после тщательного анализа, который имеет смысл.

Так как я могу жить без jQuery, а насколько хороша эта поддержка?

document.ready

Даже когда я использую jQuery, если я (или моя компания) имеет контроль над проектом, я очень, очень редко использую document.ready (или сокращенный вариант $(function)).

Потому что я помещал весь мой JavaScript вниз моей DOM перед закрывающим тегом </body>. Поэтому я всегда знаю, что DOM будет скомпилирован к этой точке.

Надеюсь, это известно на данный момент, но JavaScript блокирует рендеринг. Поместите свой JavaScript выше контента и свои серверные остановки, и Вы получете пустую страницу. Я использовал этот пример много раз раньше, но «значок» Twitter давно использовал для встраивания в Ваш HTML. Их сайт часто терял бы работоспособность, и в моём блоге (также перенося значок Twitter) удавился бы от пустой страницы — создавая впечатление, что мой сайт не работает.

.attr('value') и .attr('href')

Мне грустно, когда я вижу jQuery для извлечения значения из элемента input:

$('input').on('change', function () {
	var value = $(this).attr('value');

	alert('Новое значение: ' + value);
});

Почему? Потому что Вы можете получить значение, используя this.value. Что еще более важно — Вам следует думать о том, как Вы используете JavaScript библиотеку. Не вызывайте излишне jQuery, если он Вам не нужен.

На самом деле, дело не в jQuery — дело в практике. Код должен читаться проще:

$('input').on('change', function () {
	alert('Новое значение: ' + this.value);
});

Также принято использовать jQuery, чтобы получить href из ссылки: $(this).attr('href'), но Вы также можете легко получить это от знания DOM: this.href. Тем не менее, заметьте, что this.href отличается, это абсолютной URL, а мы говорим о DOM API, а не об элементе. Если Вам нужно значение атрибута (как это предлагается в версии jQuery), Вам понадобится this.getAttribute('href').

Далее есть установка класса на элемент, Вам не нужно jQuery для этого, если Вы просто добавляете класс.

Я видел это раньше:

	<script src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
	<script>
		$('body').addClass('hasJS');
	</script>

Но почему бы не:

</head>
<body>
	<script>
		document.body.className = 'hasJS';
	</script>

Вероятно, наиболее важное изменение в варианте ниже — нет никакого jQuery включения вначале, чтобы установить имя класса на body, да бы определить, доступен ли JavaScript.

Если у body уже имеется класс, достаточно добавить (jQuery должен считывать свойство className тоже): document.body.className += 'hasJS'.
И вот тут мы начинаем работать c проблемами имён классов и отслеживания, какие классы они имеют и не имеют. Но браузеры имеют эту функцию изначально тоже.

classList для add, remove и toggle

HTML5 classList поддержка присутствует во всех последних браузерах (обратите внимание, что в IE9 её нет — но это тот случай, когда я мог бы выбрать polyfill).
Вместо:

$('body').addClass('hasJS');
// или
document.body.className += ' hasJS';

Мы можем сделать:

document.body.classList.add('hasJS');

Разве не симпатично?

Что касается удаления:

$('body').removeClass('hasJS');
// или какой-либо сумасшедшее регулярное выражение

Или мы можем сделать:

document.body.classList.remove('hasJS');

Но более внушительней нативная поддержка переключателя:

document.body.classList.toggle('hasJS');
// и
document.body.classList.contains('hasJS');

Чтобы установить несколько классов, Вы можете добавить больше значений:

document.body.classList.add('hasJS', 'ready');

Что действительно отстойно, так это странные проблемы при изпользовании пустой строки:

document.body.classList.contains('');
// SyntaxError: DOM Exception 12

Красивый мусор. Но! С другой стороны я знаю проблемные области, и я избегаю их. В значительной степени, мы выросли работая с браузерами так или иначе.

Хранение данных

data
jQuery реализовал произвольное хранение данных вместо элементов в версии 1.2.3 и затем объектное хранение в версии 1.4 – так некоторое время назад.

В HTML5 есть нативное хранение данных вместо элементов, но есть принципиальное различие между jQuery и нативной поддержкой: объектное хранение не будет работать в наборе данных HTML5.

Но если Вы храните строки или JSON, тогда нативная поддержка совершенна:

element.dataset.user = JSON.stringify(user);
element.dataset.score = score;

Поддержка хороша, но печально никакая нативная поддержка в IE10 (хотя Вы можете добавить polyfill и будет снова работать отлично – но этоn случай при использовании набора данных).

ajax

ajax
Как я уже говорил, jQuery помог мне полностью обратить внимание на ajax. Но теперь ajax довольно прост. Несомненно, у меня нет всех дополнительных опций, но довольно часто, чем нет, я выполняю XHR GET или POST, используя JSON.

function request(type, url, opts, callback) {
	var xhr = new XMLHttpRequest(),
		fd;

	if (typeof opts === 'function') {
		callback = opts;
		opts = null;
	}

	xhr.open(type, url);

	if (type === 'POST' && opts) {
		fd = new FormData();

		for (var key in opts) {
			fd.append(key, JSON.stringify(opts[key]));
		}
	}

	xhr.onload = function () {
		callback(JSON.parse(xhr.response));
	};

	xhr.send(opts ? fd : null);
}

var get = request.bind(this, 'GET');
var post = request.bind(this, 'POST');

Это коротко и ясно. XHR не сложный и он хорошо задокументирован в наше время. Но что еще более важно, наличие понимания того, как это действительно работает и что может сделать XHR, дает нам больше.

Что на счёт прогресса? Что на счёт событий прогресса загрузки? Что на счёт того, чтобы отправить исходящий поток как ArrayBuffer? Что на счёт CORS и xml-requested-with заголовка?

Вам потребуется прямой доступ к объекту XHR для этого (я знаю, что Вы можете добиться этого с помощью jQuery), но Вы должны ознакомиться с объектом XHR и этой возможностью, потому что такие вещи, как загрузка файлов с помощью перетаскивания безумно просты с нативной функциональностью сегодня.

Наконец-то формы!

Формы
jQuery плагин валидации форм был устойчивым плагином с первых лет jQuery, и откровенно сделал работу с формами настолько проще.

Но независимо от Вашей клиентской валидации – Вы должны всегда выполнять валидацию на сервере – это остается истиной независимо от того, как Вы проверяете.

Но что, если бы Вы могли выбросить строки, строки JavaScript и плагинов, чтобы сделать валидацию адреса электронной почты как эту:

<input type="email">

Хотите сделать его обязательным полем?

<input type="email" required>

Хотите разрешить только определённые символы для пользователя?

<input pattern="[a-z0-9]">

Ерунда. Это даже идёт с вспомогательной технологической поддержкой – т.е. клавиатура будет приспосабливаться к удовлетворению символам адреса электронной почты.

Так как эти типы возвращают text, и у Вас должна быть валидация на сервере, я настоятельно призываю Вас вырвать всю свою валидаю на JavaScript и заменить на нативную HTML5 валидацию форм.

jQuery анимация VS. CSS анимация VS. JavaScript анимация

setInterval VS. requestAnimationFrame

Это не совсем поединок. CSS победил. CSS анимация обрабатывается с помощью GPU. Анимирование в JavaScript имеет дополнительный слой вычислений, фактически и есть сам JavaScript.

Даже тогда, если я пишу код сам, я стараюсь использовать requestAnimationFrame вместо setInterval анимации.

У Джейка Арчибальда есть несколько отличных слайдов, показывающие проблему здесь, посредством чего setInterval не будет гладким и быстро начнет отбрасывать фреймы:
setInterval

К тому же, CSS анимация проходит через того же планировщика как requestAnimationFrame, который является тем, что мы хотим использовать.

Если поддержка Вашего браузера позволяет, то используйте CSS анимацию. Несомненно, это не столь просто, как $foo.animate('slow', { x: '+=10px' }), но это будет чисто и гладко. Должно быть это известный факт, что взаимодействие с DOM дорого обходится. Если Вы анимируете x положение элемента путём обновления атрибута el.style.left, Вы идёте назад и вперед для анимации.

Однако, если Вы можете просто сделать foo.classList.add('animate'), CSS класс animate перейдет left положение элемента. И у Вас есть контроль, если это только свойство left, или если Вы используете преимущественно аппаратное ускорение при помощи translateX с translateZ(0).

Но что касается колбека по завершению анимации, я слышу, что Вы все кричите?! Это также доступно. Хотя это немного неприятно:

el.addEventListener('webkitTransitionEnd', transitionEnded);
el.addEventListener('transitionend', transitionEnded);

Обратите внимание на нижний регистр 'e' в 'end'…

Несколько добрых людей на Twitter также указывали мне на плагин jquery.animate-enhanced, который улучшает функцию .animate, если CSS анимация доступна.

Есть также отдельный плагин под названием Transit, который дает Вам управление с помощью JavaScript для создания CSS анимаций. Реально хорошим аспектом (для меня) является поддержка сцепления. Поскольку он основывается исключительно на CSS анимации, это требует IE10 и выше.

Что наводит меня на мысль: почему этот плагин требует jQuery специально?

Плагины jQuery – просто так

jQuery плагин - просто так

Цитаты из твиттера.
Я:

Я не знаю, почему, но я действительно хочу причинить боль людям, которые пишут jQuery плагины, которые действительно не нуждаются в jQuery./стремлюсь-управлять-гневом

Ответ:

rem Я тоже, я подозреваю, что есть группа поддержки для этого. Вероятно, довольна большая.

В последнее время я работал над проектом и знал о проекте fitText.js. Я собирался бросить его, но тогда я понял, что он нуждался в jQuery.

Хм. Почему?

Он использует следующие jQuery методы:

  1. .extend
  2. .each
  3. .width
  4. .css
  5. .on (не много думал о производительности)

Вот сам плагин:

$.fn.fitText = function( kompressor, options ) {
	// установка опций
	var compressor = kompressor || 1,
		settings = $.extend({
		  'minFontSize' : Number.NEGATIVE_INFINITY,
		  'maxFontSize' : Number.POSITIVE_INFINITY
		}, options);

	return this.each(function () {

	  // сохранение объекта
	  var $this = $(this);

	  // resizer() - изменяет размер элементов, взяв ширину объекта, делённую на компрессор * 10
	  var resizer = function () {
		$this.css('font-size', Math.max(Math.min($this.width() / (compressor * 10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)));
	  };

	  // Вызываем один раз для установки.
	  resizer();

	  // Вызываем при изменении размера. Opera пересылает изменение размеров по умолчанию.
	  $(window).on('resize orientationchange', resizer);

	});

  };

.extend используется с объектом, у которого есть всего две опции, так что я переписал бы, чтобы читалось:

if (options === undefined) options = {};
if (options.minFontSize === undefined) options.minFontSize = Number.NEGATIVE_INFINITY;
if (options.maxFontSize === undefined) options.maxFontSize = Number.POSITIVE_INFINITY;

return this.each раньше циклично выполнялся по узлам (nodes). Давайте предположим, что мы хотим, чтобы этот код работал самостоятельно, тогда наша функция fitText получила бы список узлов (так как мы не будем объединять в цепочку):

var length = nodes.length,
 	i = 0;

// хотелось бы использовать [].forEach.call, но нет поддержки в IE8
for (; i < length; i++) {
	(function (node) {
		// там, где мы использовали `this`, теперь мы используем `node`
		// ...
	})(nodes[i]);
}

$this.width() получает ширину контейнера для изменения размеров текста. Таким образом, нам нужно получить вычисляемые стили и захватить ширину:

	// resizer() - изменяет размер элементов, взяв ширину объекта, делённую на компрессор * 10
	var resizer = function () {
	var width = node.clientWidth;

	// ...
};

Действительно важно отметить, что замена .width() для .clientWidth не будет работать во всех плагинах. Правильной эта замена будет только в случае частной проблемы, которую я решал (что повторяет мою точку зрения: используйте правильный инструмент для работы).

$this.css используется в качестве метода set, так что это просто случай установки стиля:

node.style.fontSize = Math.max(...);

$(window).on('resize', resizer) случай присоединения обработчика событий (отметьте, что Вы хотели бы addEvent также для поддержки IE8):

window.addEventListener('resize', resizer, false);

Фактически, я пошел дальше и сохранил изменённые размеры в массиве, и при изменении размера выполнил цикл по массиву, выполняющий функции изменения размера.

Несомненно, немного больше работы, но это было бы также просто обновить это изменение, чтобы исправить в плагине jQuery такую поддержку как обновление, вместо того, чтобы делать его предпосылкой.

Шумная проповедь скоро будет закончена: меня также раздражает, когда я вижу, что polyfill требует jQuery – но я знаю, что контраргумент этому то, что у jQuery есть чрезвычайно высокое внедрение, поэтому это, возможно, оправдывает зависимость.

В заключение

Моя цель состояла в том, чтобы показать Вам что, пока jQuery протянул мне такую огромную руку помощи за эти годы (особенно в те годы бедной совместимости), что нативная поддержка браузера — длинный путь к выполнению большого количества общих потоков операций, которые я имел при записи JavaScript, чтобы «вытворять такое» с DOM.

Забудьте о X фиче, не работающей в браузере Y – подойдите к ней с точки зрения: Что я пытаюсь решить? Какой самый лучший инструмент для этой работы? Какая моя аудитория?

Я всё еще верю в прогрессивное улучшение, но я не верю в наклон назад, чтобы поддерживать воображаемую базу пользователей (потому что у нас нет данных, чтобы сказать, какие браузеры наши пользователи используют).

Google (последнее, что я читал) поддерживают последнее минус 1. Я стараюсь начать с той же базовой поддержки тоже.

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

С этим тогда я закругляюсь, и я надеюсь, что это было полезно для Вас.

Возможно некоторые из Вас уже знали всё это (хотя я подвергну сомнению, почему Вы следите/читаете это!), надо надеяться, я показал некоторым из Вас, что есть мир вне jQuery и что Вы можете начать использовать его сегодня в каких-либо Ваших проектах.

Возможно некоторые из Вас плохо знакомы с jQuery – я надеюсь, что Вы продолжаете смотреть дальше на то, на что способны JavaScript и DOM.

Но для большинства Вас: Я пою в хоре. Вы уже окружены. Вы уже верите в стандарты, делая это правильно, учась и улучшая себя. Именно те люди, которые не получают эту информацию, которой нужна Вам, чтобы помочь.

Вы должны совместно использовать свой опыт с другими вокруг Вас. Теперь Вы эксперт, и Вам решать помочь тем вокруг Вас, помочь им подходить к Вашим стандартам и вне.
Конференции будут искать новых докладчиков, новых экспертов: Вы — тот человек.

Для любителей пролистывать в самый конец

Настало время использовать нативные техногии браузеров, когда появилась возможность обходиться без jQuery. Так как статья довольна большая, то напрашивается подвести итоги:

  • Используйте querySelectorAll для навигации по DOM.
  • Думайте о том, когда использовать jQuery/любую другую библиотеку.
  • Используйте встроенные средства HTML5 для валидации форм, но не забывайте проверку на сервере.
  • Забудьте про document.ready, помещайте код перед закрывающимся тегом </body>.
  • Используйте this.value для получения значения.
  • Попробуйте .classList для взаимодействия с классами вместо .css и .className.
  • Вникните в то, как работает ajax.
  • Используйте анимацию CSS вместо анимации на JavaScript.
  • Можете ли Вы обойтись без jQuery с самого начала?

В моё личное заключение хочу поздравить Хабравчан с Днём Фрилансера!

Автор: GC92

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js