От автора:
Если и есть что-то плохое в jQuery, это то, что начальный уровень настолько низок, что поражает тех, кто совершенно не знаком с JavaScript. С одной стороны, это фантастика. Однако, с обратной стороны, следствием этого является поверхностные знания и, если честно, отвратительно плохой код (который я и сам писал!).
Но это нормально; ужасающе жалкий код, который заставил бы вздохнуть даже вашу бабушку – что-то вроде обряда посвящения. Главное – совершить восхождение на эту гору, и это как раз то, что мы сегодня обсудим в этом учебнике.
1. Методы возврата объекта jQuery Object
Важно помнить, что большинство методов возвратят объект jQuery. Это чрезвычайно полезно и позволяет строить широко используемые цепочки функций.
1 2 3 4 |
$someDiv .attr('class', 'someClass') .hide() .html('new stuff'); |
Зная, что объект jQuery всегда возвращается, время от времени мы можем пользоваться этим для удаления избыточного кода. Например, рассмотрим следующее:
1 2 |
var someDiv = $('#someDiv'); someDiv.hide(); |
Причина, по которой мы "кэшируем" место размещения элемента someDiv, состоит в ограничении количества раз, когда мы вынуждены прослеживать DOM этого элемента.
Код вверху полностью хорош; однако вы могли бы почти так же легко объединить две строки в одну для достижения того же итога.
1 |
var someDiv = $('#someDiv').hide(); |
Таким образом, мы все еще скрываем элемент someDiv, но этот метод так же, как мы и узнали, возвращает объект jQuery — на который мы затем ссылаемся через переменную someDiv.
2. Селектор Find
Если ваши селекторы не настолько смехотворно плохи, jQuery проделает фантастическую работу по их оптимизации, и, в общем, вам не нужно слишком о них беспокоиться. Однако, наряду со сказанным, вы можете позволить себе некоторое количество усовершенствований, слегка улучшающих производительность вашего скрипта.
Одним из таких решений является использование, когда это возможно, метода find(). Основная идея – это отход от принуждения jQuery использовать движок Sizzle, когда это не является необходимым. Конечно, будут моменты, когда это невозможно — и это нормально; но, если вам не нужны дополнительные издержки, не нарывайтесь на них.
1 2 3 4 5 |
// хорошо для современных браузеров, хотя Sizzle не начинает "запускаться" $('#someDiv p.someClass').hide(); // хорошо для всех браузеров, а Sizzle не включает зажигание. $('#someDiv').find('p.someClass').hide(); |
У последних современных браузеров имеется поддержка QuerySelectorAll,что позволяет передавать селекторы, аналогичные CSS, не нуждаясь в jQuery. Сам по себе jQuery с тем же успехом использует эту функцию, когда это возможно.
Однако старые браузеры, а именно IE6/IE7, естественно, не обеспечивают поддержку этой функции. Значит, такие более сложные селекторы запускают движок jQuery Sizzle – несомненно замечательный, но требующий больших ресурсов.
Sizzle – блестящая реализация, которую я, возможно, никогда не пойму. Тем не менее, если вкратце, он сначала берет селектор и превращает его в "массив", состоящий из каждого компонента вашего селектора.
1 2 |
// смутное представление о том, как это работает ['#someDiv, 'p']; |
Затем справа налево начинает расшифровывать каждый элемент стандартными регулярными выражениями. Кроме всего прочего, еще это означает, что крайняя справа часть вашего селектора должна быть как можно более конкретной — например, id или название тэга.
Итак, когда это возможно:
Старайтесь сделать свои селекторы простыми
Используйте метод find(). Таким образом, вместо использования Sizzle, можно продолжить применять встроенные функции браузера.
Как можно лучше оптимизируйте правую часть селектора при использовании Sizzle.
Контекст вместо этого?
Также можно использовать в своих селекторах текущий контекст, например:
1 |
$('.someElements', '#someContainer').hide(); |
Этот код предписывает jQuery упаковать в коллекцию множество всех элементов класса someElements — являющихся дочерними элементами someContainer — внутри jQuery. Использование контекстов – полезный способ ограничения обхода узлов DOM, поскольку "за кулисами" jQuery в этом случае использует метод find.
1 2 3 |
$('#someContainer') .find('.someElements') .hide(); |
Подтверждение:
1 2 3 4 5 |
// ДЕСКРИПТОР: $(expr, context) // (который эквивалентен: $(context).find(expr) } else { return jQuery( context ).find( selector ); } |
3. Не злоупотребляйте $(this)
Не зная о всевозможных свойствах и функциях DOM, можно легко без нужды злоупотребить объектом jQuery. Например:
1 2 3 4 |
$('#someAnchor').click(function() { // чепуха alert( $(this).attr('id') ); }); |
p>Если объект jQuery нужен вам только для того, чтобы иметь доступ к атрибуту id тэга-привязки, это расточительно. Лучше придерживаться "обычного" JavaScript’а.
1 2 3 |
$('#someAnchor').click(function() { alert( this.id ); }); |
Пожалуйста, обратите внимание на то, что есть три атрибута, к которым всегда следует получать доступ c помощью jQuery: "src," "href," и "style.". Для этих атрибутов в старых версиях IE требуется использование getAttribute.
Подтверждение:
1 2 3 4 5 6 7 8 9 |
// источник jQuery var rspecialurl = /href|src|style/; // ... var special = rspecialurl.test( name ); // ... var attr = !jQuery.support.hrefNormalized && notxml && special ? // некоторые атрибуты в IE требуют специального вызова elem.getAttribute( name, 2 ) : elem.getAttribute( name ); |
Множественные объекты jQuery
Еще хуже процесс неоднократных запросов к DOM и создания множественных объектов jQuery.
1 2 3 |
$('#elem').hide(); $('#elem').html('bla'); $('#elem').otherStuff(); |
Будем надеяться, что вы уже осведомлены о том, насколько это неэффективный код. Если нет, ничего страшного; все мы учимся. Решение состоит либо в реализации последовательности, либо в "кэшировании" местоположения #elem.
1 2 3 4 5 6 7 8 9 10 11 |
// Это работает лучше $('#elem') .hide() .html('bla') .otherStuff(); // Или это, если вы по какой-то причине предпочитаете так. var elem = $('#elem'); elem.hide(); elem.html('bla'); elem.otherStuff(); |
4. Сокращенная запись метода jQuery Ready
Обработка события ready документа при помощи jQuery до смешного просто.
1 2 3 |
$(document).ready(function() { // давайте встанем в восторге }); |
Хотя весьма возможно, что вы могли наткнуться на другую, более запутанную обертывающую функцию.
1 2 3 |
$(function() { // давайте встанем в восторге }); |
Хотя последняя отчасти менее удобочитаема, два отрывка вверху идентичны. Не верите? Просто взгляните в исходники jQuery.
1 2 3 4 5 |
// ДЕСКРИПТОР: $(function) // путь напрямик к документу готов if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } |
rootjQuery – просто ссылка на корневой jQuery(document). При передаче селектора функции jQuery она определит тип селектора: string, tag, id, function, и т.д. Если была передана функция, jQuery вызывает свой метод ready() и передает вашу анонимную функцию как селектор.
5. Создавайте безопасный код
Разрабатывая код для дистрибуции, очень важно исключить любой возможный конфликт имен. Что произойдет, если какой-то скрипт, импортированный вслед за вашим, тоже имел бы функцию $? Полная фигня!
Решение – либо вызвать jQuery noConflict(), либо хранить свой код в самовызываемой анонимной функции, а затем передать ей jQuery.
Метод 1: NoConflict
1 2 3 4 5 6 |
var j = jQuery.noConflict(); // здесь вместо $ мы используем j. j('#someDiv').hide(); // строка внизу сошлется на функцию $ другой библиотеки. $('someDiv').style.display = 'none'; |
Будьте осторожны с этим методом и постарайтесь не использовать его при дистрибуции своего кода. Он реально собьет с толку пользователя вашего скрипта!
Метод 2: Передача jQuery
1 2 3 |
(function($) { // в этой функции $ всегда будет ссылаться на jQuery })(jQuery); |
Последние круглые скобки внизу автоматически вызывают функцию – function(){}(). Однако при вызове функции мы также передаем jQuery, который затем представлен $.
Метод 3: Передача $ посредством метода Ready
1 2 3 4 5 |
jQuery(document).ready(function($) { // $ ссылается на jQuery }); // $ либо не определено, либо ссылается на функцию другой библиотеки. |
6. Будьте разумны
Помните – jQuery это просто JavaScript. Не думайте, что он способен исправить ваш плохой код.
Это значит, что если мы должны оптимизировать такие вещи, как предложения for из JavaScript, то же самое касается метода jQuery each. А почему нет? Это просто вспомогательный метод, который затем «за кулисами» создает предложение for.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// источник метода jQuery each each: function( object, callback, args ) { var name, i = 0, length = object.length, isObj = length === undefined || jQuery.isFunction(object); if ( args ) { if ( isObj ) { for ( name in object ) { if ( callback.apply( object[ name ], args ) === false ) { break; } } } else { for ( ; i < length; ) { if ( callback.apply( object[ i++ ], args ) === false ) { break; } } } // специальный быстрый случай самого обычного использования each } else { if ( isObj ) { for ( name in object ) { if ( callback.call( object[ name ], name, object[ name ] ) === false ) { break; } } } else { for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} } } return object; } |
Ужасно
1 2 3 |
someDivs.each(function() { $('#anotherDiv')[0].innerHTML += $(this).text(); }); |
Ищет anotherDiv в каждой итерации цикла
Дважды подхватывает свойство innerHTML
Создает новый объект jQuery только для того, чтобы иметь доступ к тексту элемента.
Гораздо лучше
1 2 3 4 5 6 7 |
var someDivs = $('#container').find('.someDivs'), contents = []; someDivs.each(function() { contents.push( this.innerHTML ); }); $('#anotherDiv').html( contents.join('') ); |
Таким образом, единственное действие, которое мы выполняем внутри метода each (for) – это добавление в массив нового ключа…в противоположность запросу к DOM, использованию дважды свойства innerHTML элемента и т.д.
Этот совет больше касается вообще JavaScript, чем конкретно jQuery. Главное помнить, что jQuery не исправляет плохой код.
Фрагменты документа
В подобных ситуациях имеется альтернатива– использование фрагментов документа.
1 2 3 4 5 6 7 8 9 10 11 |
var someUls = $('#container').find('.someUls'), frag = document.createDocumentFragment(), li; someUls.each(function() { li = document.createElement('li'); li.appendChild( document.createTextNode(this.innerHTML) ); frag.appendChild(li); }); $('#anotherUl')[0].appendChild( frag ); |
Суть в том, что существует множество способов выполнения таких простых задач, как эта, и каждый имеет свои преимущества и недостатки в плане производительности от браузера к браузеру. Чем больше вы придерживаетесь jQuery и изучаете JavaScript, тем чаще можете заметить, что обращаетесь к встроенным свойствам и методам JavaScript. Если так, то это отлично!
jQuery обеспечивает поразительный уровень абстракции, и его преимуществами вы должны пользоваться, но это не значит, что вас этими методами заставляют пользоваться. Например, в вышеуказанном фрагменте мы употребляем метод jQuery each. Если вы предпочтете использовать вместо него предложение for или while, тоже получится хорошо!
Наряду со сказанным помните о том, что разработчики jQuery тщательно оптимизировали эту библиотеку. Споры вокруг jQuery each() против "родного" предложения for глупы и банальны. Если вы в своем проекте используете jQuery, сэкономьте время и пользуйтесь его собственными вспомогательными методами. Они и созданы для этого!
7. Методы AJAX
Если вы только начинаете углубляться в jQuery, различные доступные в нем методы AJAX, на которые вы могли наткнуться, немного обескураживают; а не должны. Фактически, большинство из них – просто вспомогательные методы, ведущие прямиком к $.ajax.
get
getJSON
post
ajax
Как пример давайте рассмотрим getJSON, который позволяет нам получить JSON.
1 2 3 4 |
$.getJSON('path/to/json', function(results) { // обратный вызов // results содержит возвращенный объект data }); |
За кулисами этот метод сначала вызывает $.get.
1 2 3 |
getJSON: function( url, data, callback ) { return jQuery.get(url, data, callback, "json"); } |
$.get затем компилирует пересланные данные и заново вызывает "хозяйский" (как бы) метод $.ajax.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
get: function( url, data, callback, type ) { // перемещение аргументов, если аргумент data был пропущен if ( jQuery.isFunction( data ) ) { type = type || callback; callback = data; data = null; } return jQuery.ajax({ type: "GET", url: url, data: data, success: callback, dataType: type }); } |
Наконец, $.ajax выполняет большое количество работы, чтобы дать нам возможность успешно произвести асинхронные запросы через все браузеры!
Это значит – вы можете с тем же успехом использовать метод $.ajax напрямую и исключительно для всех запросов AJAX. Прочие методы – просто вспомогательные, которые, как бы то ни было, в конце концов делают то же самое. Итак, если хотите, исключите посредника. Это в любом случае незначительная проблема.
Поистине высший класс:
1 2 3 4 |
$.getJSON('path/to/json', function(results) { // обратный вызов // results содержит возвращенный объект data }); |
Еще эффективнее:
1 2 3 4 5 6 7 8 9 |
$.ajax({ type: 'GET', url : 'path/to/json', data : yourData, dataType : 'json', success : function( results ) { console.log('success'); }) }); |
8. Организация доступа к встроенным свойствам и методам
Итак, вы немного выучили JavaScript и узнали, что, к примеру, к значениям атрибутов тэгов-привязок можно получить доступ напрямую:
1 2 3 4 5 |
var anchor = document.getElementById('someAnchor'); //anchor.id // anchor.href // anchor.title // .etc |
Единственная проблема состоит в том, что это, похоже, не работает, когда вы при помощи jQuery делаете ссылки на элементы DOM, верно? Ну конечно, нет.
Не заработает
1 2 |
// неудача var id = $('#someAnchor').id; |
Итак, если вам понадобится организовать доступ к атрибуту href (или для этого к любому другому "родному" свойству или методу), то у вас имеется некоторое количество вариантов.
1 2 3 4 5 6 7 8 9 10 11 12 |
// ВАРИАНТ 1 – используйте jQuery var id = $('#someAnchor').attr('id'); // ВАРИАНТ 2 – получите доступ к элементу DOM var id = $('#someAnchor')[0].id; // ВАРИАНТ 3 – используйте метод jQuery get var id = $('#someAnchor').get(0).id; // ВАРИАНТ 3А – не передавайте указатель get anchorsArray = $('.someAnchors').get(); var thirdId = anchorsArray[2].id; |
Методget чрезвычайно полезен, так как может преобразовать коллекцию jQuery в массив.
9. Отслеживайте запросы AJAX при помощи PHP
Естественно, для большого количества своих проектов нельзя полагаться только на JavaScript для таких вещей, как проверка данных (валидация) или запросы AJAX. А что если JavaScript отключен? Поэтому обычная методика – это определить, был ли сделан запрос AJAX на стороне сервера с помощью используемого вами языка программирования.
jQuery делает эту задачу простой, устанавливая заголовок из метода $.ajax.
1 2 3 4 5 |
// установите заголовок так, чтобы вызываемый скрипт знал, что это XMLHttpRequest // просто отправьте заголовок, если это не удаленный XHR if ( !remote ) { xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); } |
Когда заголовок установлен, мы можем использовать PHP (или любой другой язык) для его проверки и соответствующей обработки. Для этого мы проверяем значение $_SERVER[‘HTTP_X_REQUESTED_WITH’].
Оболочка
1 2 3 |
function isXhr() { return $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'; } |
10. jQuery и $
Когда-нибудь интересовались, зачем/как можно взаимозаменяемо использовать jQuery и $? Чтобы узнать ответ, откройте исходники jQuery и прокрутите до самого конца. Там вы увидите:
1 |
window.jQuery = window.$ = jQuery; |
Весь скрипт jQuery, конечно, упакован в самоисполняющуюся функцию, которая позволяет скрипту как можно сильнее ограничить количество глобальных переменных. Тем не менее, это также значит, что объект jQuery недоступен вне анонимной функции-упаковщика.
Чтобы это исправить, jQuery публикуется в глобальном объекте window и, в процессе, еще создается псевдоним — $.
11. Условная загрузка jQuery
Стандартный текст HTML5 Boilerplate предлагает изящную однострочную конструкцию, которая загрузит локальную копию jQuery в случае, если по какой-то причине выбранный вами CDN не функционирует.
1 2 3 |
<!-- Grab Google CDN jQuery. fall back to local if necessary --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> <script>!window.jQuery && document.write('<script src="js/jquery-1.4.2.min.js"><\/script>')</script> |
"Озвучим" вышеуказанный код: если window.jQuery не определен, должна существовать проблема с закачиванием скрипта из CDN. В этом случае переходим к правой части оператора && и вставляем скрипт, ссылающийся на локальную версию jQuery.
12. Фильтры jQuery
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<script> $('p:first').data('info', 'value'); // populates $'s data object to have something to work with $.extend( jQuery.expr[":"], { block: function(elem) { return $(elem).css("display") === "block"; }, hasData : function(elem) { return !$.isEmptyObject( $(elem).data() ); } } ); $("p:hasData").text("has data"); // grabs paras that have data attached $("p:block").text("are block level"); // grabs only paragraphs that have a display of "block" </script> |
Примечание: jQuery.expr[‘:’] — просто псевдоним для jQuery.expr.filters.
13. Одиночная функция Hover
Что касается jQuery 1.4, мы теперь можем передать методу hover единственную одиночную функцию. До этого требовались два метода — in и out.
До:
1 2 3 4 5 |
$('#someElement').hover(function() { // mouseover }, function() { // mouseout }); |
Теперь:
1 2 3 |
$('#someElement').hover(function() { // здесь можно использовать метод toggle(), если он применим }); |
Обратите внимание, что это не подход "старое против нового". Много раз вам по-прежнему понадобится передавать hover две функции, и это абсолютно приемлемо. Тем не менее, если вам нужно всего лишь переключить какой-либо элемент (или что-то вроде этого), передача одиночной анонимной функции сэкономит некоторое количество символов!
14. Передача объекта-атрибута
Начиная с jQuery 1.4 теперь можно передать объект как второй параметр функции jQuery. Это полезно, когда нужно вставить в DOM новые элементы. Например:
До:
1 2 3 4 5 6 |
$('<a />') .attr({ id : 'someId', className : 'someClass', href : 'somePath.html' }); |
После:
1 2 3 4 5 |
$('</a>', { id : 'someId', className : 'someClass', href : 'somePath.html' }); |
Это не только экономит несколько знаков, но также способствует тому, чтобы код стал чище. Вдобавок к атрибутам элементов мы можем даже передать специфические атрибуты и события jQuery, вроде click или text.
Спасибо за прочтение!
Автор: Jeffrey Way
Редакция: Рог Виктор и Андрей Бернацкий. Команда webformyself.
E-mail: contact@webformyself.com
Проект webformyself.com — Как создать свой сайт. Основы самостоятельного сайтостроения
P.S. Хотите опубликовать интересный тематический материал и заработать? Если ответ «Да», то жмите сюда.
Комментарии (1)