Закрепляем jQuery — 25 отличных советов

:

Я использую предыдущий пример и изменю его таким образом, что каждый элемент «li» будет иметь уникальный класс, добавленный к нему. Затем я выберу в цикле все элементы (каждый по одному разу).

В точности как я и предполагал, браузер неслабо подвис и закончил выполнение только через 5066 миллисекунд (более 5 секунд). Так что я модифицировал код, дав каждому элементу id вместо класса и затем также выбрал каждый в цикле, но уже по id.

В этот раз — всего 61 миллисекунду. Примерно в 100 раз быстрее.

По-умолчанию, когда вы используете селектор типа $('.myDiv'), произойдет обращение ко всему DOM-документу, что, в определенных случаях (например, при большой странице), может весьма существенно отразиться на производительности.

Функция jQuery принимает второй параметр, когда производит выборка.

<i>jQuery(expression, context)</i>

Передавая контекст в селектор, вы сообщаете ему элемент, с которого нужно начинать искать. Таким образом, необходимость обращения ко всему DOM-документу отпадает.

Чтобы продемонстрировать это, снова возьмем пример из первого совета. Он создает маркированный список из 1000 элементов, каждый из которых имеет свой класс. Затем скрипт в цикле выбирает каждый элемент. Помните, что при выборке по классу, у нас заняло около 5 секунд, чтобы выбрать все 1000 элементов.

var selectedItem = $('.listItem' + i);  

Затем, я добавил контекст, чтобы jQuery использовал селектор только в пределах маркированного списка. Вроде этого:

var selectedItem = $('.listItem' + i, $('.myList'));  

Это заняло 3818 миллисекунд, что само по себе все еще адски медленно, но это уже более, чем 25%-й прирост в скорости при минимальной модификации селектора.

10. Используйте последовательности вызовов методов


Одна из самых клевых фишек в jQuery — это возможность выстраивать последовательности вызовов методов. Например, если вы хотите поменять класс элемента:
$('myDiv').removeClass('off').addClass('on');  

Скорее всего, вы выучили это в первые же 5 минут использования jQuery, но это еще не все. Во-первых, это прекрасно работает с переносами строк (поскольку jQuery — это все же JavaScript), что означает, что вы можете писать красивый код вроде этого:

$('#mypanel')  
    .find('TABLE .firstCol')  
    .removeClass('.firstCol')  
    .css('background' : 'red')  
    .append('<span>Теперь эта ячейка красная</span>');

Использование последовательностей также само по себе помогает вам ограничить использование селекторов.

Но и это еще не все. Скажем, вы хотите выполнить несколько функций на элементе, но одно из первых же применений изменило элемент каким-либо образом, например:

$('#myTable').find('.firstColumn').css('background','red');  

Мы выбрали таблицу, углубились дальше, чтобы найти ячейки с классом «firstColumn» и покрасили их в красный цвет.

Представим, что нам нужно покрасить все ячейки с классом «lastColumn» в синий. Поскольку мы использовали функцию find(), мы отфильтровали все ячейки которые не помечены классом «firstColumn», поэтому нам нужно повторно использовать селектор для получения элемента таблицы и мы не можем продолжить выстраивать последовательность сходу, верно? К счастью, в jQuery имеется функция end(), которая возвращает нас к предыдущей нетронутой выборке, так что можно продолжить нашу последовательность:

$('#myTable')  
    .find('.firstColumn')  
        .css('background','red')  
    .end()  
    .find('.lastColumn')  
        .css('background','blue');  

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

$.fn.makeRed = function() {  
    return $(this).css('background', 'red');  
}  

$('#myTable').find('.firstColumn').makeRed().append('hello');  

Просто, правда?

11. Научитесь правильно использовать анимацию


Когда я только начал использовать jQuery, я был в восторге от простоты в использовании встроенной анимации вроде slideDown() и fadeIn() для получения клевых эффектов. Очень просто пойти чуть дальше, поскольку метод jQuery animate() очень прост для использования и одновременно очень крут. Фактически, если посмотреть на исходники jQuery, можно увидеть, что все эти методы — эдакие мини-абстракции, частные случаи использования функции animate().
slideDown: function(speed,callback){  
    return this.animate({height: "show"}, speed, callback);  
},  
  
fadeIn: function(speed, callback){  
    return this.animate({opacity: "show"}, speed, callback);  
}  

Метод animate() просто берет любой CSS-стиль и плавно трансформирует его из одного значения в другое. Таким образом, можно поменять ширину, высоту, прозрачность, цвет фона, отступы, цвет, размер шрифта, да и вообще, все, что пожелается.

Вот, к примеру, так вот просто сделать анимированное меню, пункты которого увеличиваются до 100 px в высоту при наведении мышкой:

$('#myList li').mouseover(function() {  
    $(this).animate({"height": 100}, "slow");  
});  

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

$('#myBox').mouseover(function() {  
    $(this).animate({ "width": 200 }, "slow");  
    $(this).animate({"height": 200}, "slow");  
});  

Если вы хотите, чтобы эффекты протекали одновременно, тогда просто укажите оба стиля в параметрах объекта и сделайте один вызов. Как-то вот так:

$('#myBox').mouseover(function() {  
    $(this).animate({ "width": 200, "height": 200 }, "slow");  
});  

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

12. Научитесь назначать и делегировать события


С jQuery как нельзя просто обработчики события назначаются элементам DOM, что замечательно, но добавление чрезмерного числа обработчиков снижает производительность. Делегирование обработки событий позволяет добавлять меньше событий, «прослушиваемых» элементом при аналогичном с точки зрения функциональности результате. Лучший способ понять — увидеть:
$('#myTable TD').click(function(){  
    $(this).css('background', 'red');  
});  

Простая функция, которая превращает ячейки таблицы в красные, когда вы кликаете на этих ячейках. Скажем, у вас есть небольшая таблица с 10 колонками и 50 рядами, но это значит, что уже 500 обработчиков события «ждут своего часа». Может быть, куда уместнее будет назначить только одно событие всей таблице, а затем, когда произошел клик по таблице, заставить обработчик определить, какая именно ячейка вызвала событие?

Именно это и называется делегированием событий и это очень просто реализовать:

$('#myTable').click(function(e) {  
    var clicked = $(e.target);  
    clicked.css('background', 'red');  
});  

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

У делегирования событий есть еще один мега-существенный бонус. Когда вы привязываете обработчик к набору элементов, он присоединяется к тем и только к тем элементам, которые были в этом наборе в момент назначения события. Если добавить новые элементы в DOM, которые вполне себе будут выбираться селектором, они не будут иметь обработчиков. Понимаете, к чему я клоню? Их придется назначать и переназначать постоянно, если элементы меняются.

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

На момент написания статьи, автором использовалась версия 1.2.6, однако начиная с версии 1.3, в jQuery появился новый функционал, адресованный как раз проблеме переназначения событий — jQuery Event/live.

13. Используйте классы для сохранения состояния


Это наиболее простой способ хранить информацию о кусочке html. jQuery отлично манипулирует элементами, основываясь на их классах, так что если вам нужно сохранить информацию о состоянии элемента, то почему бы не добавить дополнительный класс для того, чтобы сохранить его?

А вот и пример. Мы хотим создать разворачивающееся меню. Когда мы нажимаем на кнопку, мы хотим, чтобы панель появилась со slideDown(), если она закрыта и наоборот — исчезла со slideUp(), если открыта. Начнем с HTML:

<div class="menuItem expanded">  
    <div class="button">  
        click me  
    </div>  
    <div class="panel">  
        <ul>  
            <li>Пункт меню 1</li>  
            <li>Пункт меню 2</li>  
            <li>Пункт меню 3</li>  
        </ul>  
    </div>  
</div>  

Очень просто! Мы просто добавили дополнительный класс к элементу-обертке (div), который не имеет иной роли, кроме как сообщить нам состояние элемента. Так что, все что нам нужно — обработчик события «onclick», который производит slideUp() или slideDown() соответствующей панельки, когда кнопка нажата.

$('.button').click(function() {  
  
    var menuItem = $(this).parent();  
    var panel = menuItem.find('.panel');  
  
    if (menuItem.hasClass("expanded")) {  
        menuItem.removeClass('expanded').addClass('collapsed');  
        panel.slideUp();  
    }  
    else if (menuItem.hasClass("collapsed")) {  
        menuItem.removeClass('collapsed').addClass('expanded');  
        panel.slideDown();  
    }  
});  

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

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

14. Совсем круто — используйте встроенный в jQuery метод data() для сохранения состояния


По определенным причинам, это не так хорошо документировано, но в jQuery имеется внутренний метод data(), который может быть использован для хранения информации в парах «ключ/значение», в соответствии с любым DOM-элементом. Хранение любых данных также просто, как вот это:
$('#myDiv').data('currentState', 'off');  

Мы можем улучшить пример из прошлого совета. Мы используем тот же HTML (кроме класса «expanded») и используем функцию data().

$('.button').click(function() {  
  
    var menuItem = $(this).parent();  
    var panel = menuItem.find('.panel');  
  
    if (menuItem.data('collapsed')) {  
        menuItem.data('collapsed', false);  
        panel.slideDown();    
    }  
    else {  
        menuItem.data('collapsed', true);  
        panel.slideUp();  
    }  
});  

Я уверен, вы согласитесь, что это куда более кошерно :) Для дополнительной информации по data() и removeData(), смотрите эту страницу про jQuery internals.

15. Пишите собственные селекторы


jQuery содержит массу встроенных селекторов для выборки элементов по id, классу, тегу, атрибуту и многому другому. Но что делать, когда необходимо выбрать элементы по какому-либо другому критерию и у jQuery нет подходящего инструмента?

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

До сих пор, лучший способ продемонстрировать это — пример:

$.extend($.expr[':'], {  
    over100pixels: function(a) {  
        return $(a).height() > 100;  
    }  
});  
  
$('.box:over100pixels').click(function() {  
    alert('Элемент, который вы потревожили своим кликом, в высоту более 100 пикселов');
});  

Первый блок кода создает собственный селектор, который ищет все элементы, которые выше 100 пикселов. Второй блок просто использует первый для назначения обработчиков для всех этих элементов.

Я не буду здесь еще более детально рассматривать все это, но вы уже можете представить, насколько суперкрут такой инструмент и если порыскать в Гугле на тему «custom jquery selector», можно найти много интересного.

16. Подготавливайте HTML и модифицируйте его, когда страница загружена


Заголовок может быть не столь понятен, но этот совет потенциально улучшит ваш ужасный код, уменьшит его вес и время загрузки, а кроме прочего, поможет напихать в страницу побольше ключевых слов с SEO. Возьмем вот такой вот HTML в качестве примера:
<div class="fieldOuter">  
    <div class="inner">  
        <div class="field">Это поле номер 1</div>  
    </div>  
    <div class="errorBar">  
        <div class="icon"><img src="icon.png" alt="icon" /></div>  
        <div class="message"><span>Это сообщение об ошибке</span></div>  
    </div>  
</div>  
<div class="fieldOuter">  
    <div class="inner">  
        <div class="field">Это поле номер 2</div>  
    </div>  
    <div class="errorBar">  
        <div class="icon"><img src="icon.png" alt="icon" /></div>  
        <div class="message"><span>Это сообщение об ошибке</span></div>  
    </div>  
</div>  

Это пример того, какой может быть разметка формы, слегка модифицированная, чтобы проиллюстрировать наши задачи. Я уверен, вы согласитесь, что этот код ужасен и если у вас большая форма — вы закончите с неприлично длинной и, мягко говоря, некрасивой страничкой. Куда лучше было бы иметь что-то вроде этого в HTML:

<div class="field">Это поле 1</div>  
<div class="field">Это поле 2</div>  
<div class="field">Это поле 3</div>  
<div class="field">Это поле 4</div>  
<div class="field">Это поле 5</div>  

Все, что здесь нужно — немножко уличной магии jQuery, чтобы вернуть тот самый некрасивый HTML. Смотрим:

$(document).ready(function() {  
    $('.field').before('<div class="fieldOuter"><div class="inner">');  
    $('.field').after('</div><div class="errorBar"><div class="icon">  
        <img src="icon.png" alt="icon" /></div><div class="message">  
        <span>Это сообщение об ошибке</span></div></div></div>');
});  

Конечно, не всегда рекомендуется делать так, все зависит от конкретного случая, но когда нужно получить много повторяющегося HTML-я, это может существенно снизить вес страницы и вообще оптимизировать ее и привести в порядок.

17. Используйте «отложенную загрузку» (lazy loading) для определенного контента для выигрыша в общей скорости и преимуществ для SEO


Еще один способ увеличить скорость загрузки страницы и очистить HTML, который просматривают поисковые роботы — это использовать так называемый «lazy loading» или говоря по-чукотски по-простому — отложенную загрузку целых частей, используя AJAX-запрос после того, как страница уже загружена. Пользователи смогут немедленно увидеть информацию, а поисковики — им и подавно, только информация и нужна.

Мы использовали эту технику на нашем собственном сайте. Те фиолетовые кнопочки наверху страницы подгружают 3 формы, направления и карту Google, которые увеличили бы изначальный вес страницы вдвое. Так что мы просто поместили весь HTML в статичную страницу и использовали функцию load(), чтобы загрузить все это уже после полной загрузки документа. Делается вот так:

$('#forms').load('content/headerForms.html', function() {  
    // Этот код выполнится после загрузки статичного контента
    // Сюда помещаем всякого рода обработчики и т. д.
});  

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

18. Используйте служебные функции jQuery


jQuery — это не только ослепительные эффекты. Создатель наделил его некоторыми действительно полезными методами, которые могут восполнить пробелы в репертуаре JavaScript:

jQuery utilities

В частности, кросс-браузерная поддержка общих функций для работы с массивами (в IE7 нет даже метода indexOf()!). jQuery содержит методы для итерации, фильтрации, клонированию, соединению и удалению дубликатов из массивов.

К другим труднореализуемым вещам в JavaScript относится, к примеру, задача, где нужно получить выбранный элемент из выпадающего списка. В старом добром JavaScript нам пришлось бы получить элемент «select», используя getElementById, получить дочерние элементы как массив и «пробежаться» по ним в итерации, проверяя каждый, был он выбран или нет. jQuery упрощает подобные дела:

$('#selectList').val();

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

19. Используйте «noconflict» для переименования глобального объекта «jquery», когда используете его с другими фреймворками


Многие JavaScript-фреймворки используют символ "$" в качестве краткой записи и это может может вызвать непредсказуемый результат при попытке использования нескольких библиотек сразу, на одной страничке. К счастью, есть простое решение. Функция .noconflict() обеспечивает контроль "$" и дает возможность задать собственное имя переменной, например
var $jabrajabr = jQuery.noConflict();  
$jabrajabr('#myDiv').hide();

20. Как узнать что картинки загружены?


Это еще одна из проблем, решение которой не так хорошо документировано, как хотелось бы и при этом достаточно распространена (в случаях разработки фото-галерей, «каруселей» и т. д.). Тем не менее, все просто.

Все, что нужно — использовать метод .load() на элементе «img» и вызвать в нем (методе) callback-функцию. Следующие примеры заменяют значение атрибута «src» у картинки для подгрузки новых изображений и присоединяют простую функцию загрузки:

$('#myImage').attr('src', 'image.jpg').load(function() {  
    alert('Картинка загружена');  
});

Можете проверить, alert будет вызван, когда картинка загрузится.

21. Всегда используйте последнюю версию


jQuery постоянно развивается и John Resig, его создатель, день и ночь в поисках путей улучшения производительности.

jQuery сейчас (на момент написания статьи — прим. пер.) в версии 1.2.6, но Джон уже раскрыл, что он работает над новым движком выборки — Sizzle, который увеличит скорость выборки, к примеру, в Firefox, в 4 раза. Так что, чем свежее — тем лучше.

22. Как проверить, что элемент существует?


Вам не нужно проверять, существует ли элемент на странице, до произведения манипуляций с ним, так как jQuery просто ничего не будет делать, если вы пытаетесь выбрать что-то, чего нет в DOM. Но когда вам нужно-таки проверить, было ли что-то выбрано, или как много элементов выбрано, можно использовать свойство length:
if ($('#myDiv).length) {  
    // your code  
}

Просто, но недостаточно очевидно.

23. Добавляйте класс «JS» в элемент «html»


Я узнал эту фишку от товарища Karl'а Swedberg'а, по чьим замечательным книгам я изучал jQuery.

Недавно на одну из моих предыдущих статей он оставил комментарий об этом приеме и суть в следующем…

В первую очередь, как только jQuery загрузится, мы используем его для добавления
класса «JS» к тегу «html»:

$('HTML').addClass('JS');

Поскольку, это случится, только когда JavaScript включен, вы можете использовать его для добавления CSS-стилей, которые работают только тогда, когда JavaScript работает:

.JS #myDiv{display:none;}

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

Можете прочитать его полную статью здесь.

24. Возвращайте «false» для отмены поведения по-умолчанию


Это может быть очевидно, а может быть и нет. Если у вас есть привычка делать так:
<a href="#" class="popup">Нажми меня, злодей!</a>  

… а затем назначать обработчик события вот таким вот образом:

$('.popup').click(function(){  
    // Код запуска popup'а
});

… то возможно это будет нормально работать, но ровно до тех пор, пока страничка не станет длиннее, чем ожидалось изначально. В таком случае, вы увидите, как после каждого нажатия на ссылку, страничка «прыгает» в самое начало (знак #).

Все, что нужно — отменить стандартное поведение ссылки, впрочем, как и любого другого элемента. Для этого нужно добавить «return false;» в свой обработчик, например:

$('.popup').click(function(){  
    // Код запуска popup'а
    return false;  
});

25. Короткая запись для события готовности документа


Маленький прием, который поможет сэкономить несколько символов, если использовать сокращение функции $(document).ready.

Вместо этого:

$(document).ready(function (){  
    // Ваш код 
});  

можно сделать вот так:

$(function (){  
    // Ваш код
});

На этом эпическая коллекция приемов в jQuery от товарища Jon Hobbs-Smith'а заканчивается. Надеюсь, вам удалось почерпнуть для себя что-нибудь новое.

Update. Спасибо хабраюзеру NemeZZiZZ за то, что справедливо обозвал «unordered list» маркированным списком (я перевел это как «неупорядоченный список», что, конечно, дословно, но определенно отдает чем-то чукотским).

Update 2. Спасибо хабраюзеру david_mz за указание на новые особенности jQuery, начиная с версии 1.3, которые касаются назначения и делегирования событий (см. п. 12).