Если вы читали предыдущие статьи из этой серии, то вы наверное уже пробовали разрабатывать свой плагин, если нет, то можем начать вместе…
Материалы данной статьи включены в учебник «jQuery для начинающих». Учебник распространяется бесплатно, и сопровождается интерактивными примерами.
Для начала вспомним, для чего нам нужны плагины? Мой ответ — создание повторно используемого кода, и да — с удобным интерфейсом. Давайте напишем такой код, вот простая задачка: «По клику на параграф, текст должен измениться на красный»
Javascript и даже не jQuery
Дабы не забывать истоков — начнем с реализации на нативном javascript’е:
var loader = function () { // находим все параграфы var para = document.getElementsByTagName('P'); // перебираем все, и вешаем обработчик for (var i=0,size=para.length;i<size;i++) { // обработчик para[i].onclick = function() { this.style.color = "#FF0000"; } } } // естественно, весь код должен работать после загрузки всей страницы document.addEventListener("DOMContentLoaded", loader, false);
Данный код не является кроссбраузерным, и написан с целью лишний раз подчеркнуть удобство использования фреймворка
jQuery, но еще не плагин
Теперь можно этот код упростить, подключаем jQuery и получаем следующий вариант:
$(document).ready(function(){ $('p').click(function(){ $(this).css('color', '#ff0000'); }) });
Таки jQuery плагин
С поставленной задачей мы справились, но где тут повторное использование кода? Или если нам надо не в красный, а в зеленый перекрасить? Вот тут начинается самое интересное, чтобы написать простой плагин достаточно расширить объект $.fn:
$.fn.mySimplePlugin = function () { $(this).click(function(){ $(this).css('color', '#ff0000'); }) }
Если же писать более грамотно, то нам необходимо ограничить переменную $ только нашим плагином, а так же возвращать this, чтобы можно было использовать цепочки вызовов (т.н. chaining) , делается это следующим образом:
(function($) { $.fn.mySimplePlugin = function(){ // код плагина ... return this; }; })(jQuery);
Внесу небольшое пояснение о происходящем, код (function($){…})(jQuery) создает анонимную функцию, и тут же вызывает ее, передавая в качестве параметра объект jQuery, таким образом внутри анонимной функции мы можем использовать алиас $ не боясь за конфликты с другими библиотеками — так как теперь $ находится лишь в области видимости нашей функции, и мы имеем полный контроль над ней
Добавим опцию по выбору цвета и получим рабочий плагин:
(function($) { // значение по умолчанию - ЗЕЛЁНЫЙ var defaults = { color:'green' }; // актуальные настройки, глобальные var options; $.fn.mySimplePlugin = function(params){ // при многократном вызове функции настройки будут сохранятся, и замещаться при необходимости options = $.extend({}, defaults, options, params); $(this).click(function(){ $(this).css('color', options.color); }); return this; }; })(jQuery);
Вызов:
// первый вызов $('p:first,p:last').mySimplePlugin(); // второй вызов $('p:eq(1)').mySimplePlugin({ color: 'red' });
В результате работы данного плагина, каждый клик будет изменять цвет параграфа на красный, т.к. мы используем глобальную переменную для хранения настроек, то второй вызов плагина изменят значение для всех элементов. Можно внести небольшие изменения, и разделить настройки для каждого вызова (пример):
// актуальные настройки, будут индивидуальными при каждом запуске var options = $.extend({}, defaults, params);
Работаем с коллекциями объектов
Тут все просто, достаточно запомнить — this содержит jQuery объект с коллекцией всех элементов, т.е. :
$.fn.mySimplePlugin = function(){ console.log(this); // jQuery console.log(this.length); // число элементов return this; };
Если мы хотим обрабатывать каждый элемент то соорудим следующую конструкцию:
// необходимо обработать каждый элемент в коллекции return this.each(function(){ $(this).click(function(){ $(this).css('color', options.color); }); }); // предыдущий вариант немного избыточен, // т.к. внутри функции click и так есть перебор элементов return this.click(function(){ $(this).css('color', options.color); });
Опять же напомню, если ваш плагин не должен что-то возвращать по вашей задумке — возвращайте this — цепочки вызовов в jQuery это часть магии, не стоит её ломать
Публичные методы
Так, у нас написан крутой плагин, надо бы ему еще докрутить функционала, пусть цвет регулируется несколькими кнопками на сайте. Для этого нам понадобится некий метод «color», который и будет в ответе за всё. Сейчас приведу пример кода готового плагина — будем курить вместе:
// значение по умолчанию var defaults = { color:'green' }; // наши публичные методы var methods = { // инициализация плагина init:function(params) { // актуальные настройки, будут индивидуальными при каждом запуске var options = $.extend({}, defaults, params); return this.click(function(){ $(this).css('color', options.color); }); }, // изменение цвета color:function(color) { $(this).css('color', color); }, // сброс цвета reset:function() { $(this).css('color', 'black'); } }; $.fn.mySimplePlugin = function(method){ // немного магии if ( methods[method] ) { // если запрашиваемый метод существует, мы его вызываем // все параметры, кроме имени метода прийдут в метод // this так же перекочует в метод return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { // если первым параметром идет объект, либо совсем пусто // выполняем метод init return methods.init.apply( this, arguments ); } else { // если ничего не получилось $.error( 'Метод "' + method + '" не найден в плагине jQuery.mySimplePlugin' ); } };
Теперь еще небольшой пример использование данных методов:
// вызов без параметров - будет вызван init $('p').mySimplePlugin(); // вызов метода color и передача цвета в качестве параметров $('p').mySimplePlugin('color', '#FFFF00'); // вызов метода reset $('p').mySimplePlugin('reset');
Для понимания данного кусочка кода, вы должны разобраться лишь с переменной arguments, и с методом apply (тут им целые статьи посвятили — дерзайте)
О обработчиках событий
Если ваш плагин вешает какой-либо обработчик, то лучше всего (читай всегда) данный обработчик повесить в своём собственном namespace:
return this.bind("click.mySimplePlugin",function(){ $(this).css('color', options.color); });
Данный финт позволит в любой момент убрать все ваши обработчики, или вызвать только ваш, что очень удобно:
// вызовем лишь наш обработчик $('p').trigger("click.mySimplePlugin"); // убираем все наши обработчики $('p').unbind(".mySimplePlugin");
Использование data
Если по какой-то причине вы еще не знакомы с data — то советую прочитать и усвоить незамедлительно. Если же в двух словах — это реестр данных, и все данные привязанные к какому-либо элементу лучше хранить в нем, это же правило касается и плагинов. Если вам надо сохранить состояние плагина — используйте data, если необходим кеш — используйте data, если вам необходимо сохранить … ну думаю понятно. Приведу еще примерчик связанный с инициализацией:
// функция init function() { var init = $(this).data('mySimplePlugin'); if (init) { return this; } else { $(this).data('mySimplePlugin', true); return this.bind("click.mySimplePlugin",function(){ $(this).css('color', options.color); }); } }
Источники
- Plugins/Authoring
- jQuery Plugin Patterns
- A Plugin Development Pattern
- Learning jQuery: Your First jQuery Plugin, “BubbleUP”
Цикл статей
- jQuery для начинающих
- jQuery для начинающих. Часть 2. JavaScript Меню
- jQuery для начинающих. Часть 3. AJAX
- jQuery для начинающих. Часть 4. Селекторы
- jQuery для начинающих. Часть 5. Эффекты
- jQuery для начинающих. Часть 6. События
- jQuery для начинающих. Часть 7. Пишем плагины
- jQuery для начинающих. Часть 8. Расширяем фильтры
- jQuery для начинающих. Часть 9. Пишем плагины анимации
Автор, спасибо за труд!!!
Спасибо. Отличный цикл, с удовольствием прочту эту часть.
Спасибо. Хорошая статья.
Благодарствую за сей тяжкий труд, давно хотелось написать какой-нить маленький плагин ради развлечения, но лень было копошиться по соответствующей документации, а тут… Весь материал скомпилирован в одну статью, в общем теперь-то что-то наворочу. Еще раз спасибо!
Давно хотел всё оформить в отдельные плагины, но всё как то не было толкового обзора.
Спасибо, за качественный материал!
Спасибо за материал. Ковыряюсь вот с одной идейкой динамического использования географических карт совместно с блогом. И обнаружил, что писать на чистом JS тяжело невероятно (вообще, по малоопытности, JS производит жуткое впечатление). Лучше – с фреймворком jquery(). Но еще обнаружил, что плагинов для jquery, которые использовали третью версию API google maps просто не существует. Все что есть – для второй GMAP2.
Между тем – http://code.google.com/intl/ru-RU/apis/maps/documentation/javascript/v2/reference.html – Google настоятельно рекомендует начать юзать третью версию. Такая вот беда.
Как бы осилить?.. Кто бы помог? :)
Огромное спасибо за эту серию статей! Антон, когда вы находите время для написания статей? 0_о
Спасибо отличная статья, особенно про магию с публичными методами, об этом к сожалению почти не пишут, спасибо.
Огромный респектище автору!
Убедительная просьба: Может ли автор нас поближе познакомить с неким чудом как $.widget от jQuery.UI ? Да и вообще с внутренностями УЯ. Да! И еще про namespace практически нигде нет ничего вменяемого.
Респектам не будет предела!
вот никак не могу понять смысл Array.prototype.slice.call( arguments, 1 ),
по отдельности понимаю что значит каждая из этих ф-й, но именно вот такую конструкцию не понимаю, обьясните пожалуйста. и почему не сделать вызов таким образом
return methods[ method ].call( this, Array.prototype.slice.call( arguments ));
т.е. return methods[ method ].call(this, arguments);
Потому что в случае:
в функцию передадутся все аргументы, а нужны все кроме первого (имени метода). Можно было бы
заменить не
, НО! arguments это не массив, у него нет метода slice, поэтому так страшно на первый взгляд и получается :)
Спасибо
Очень толково написано. Спасибо автору =)
Спасибо, надеюсь руки дойдут собрать плагинчики.
Спасибо
Прекрасная статья.
Уже несколько раз к ней обращался при написании плагинов.
Большое спасибо.
Спасибо.
Супер! Дает понимание, и не отвращает чрезмерной сложностью!
А то, бывает, начнешь читать, и желание изучать технологию пропадает..
Спасибо!!!
Дизайн на сайтах в примерах суперовый!
А как можно проверить – вызывался ли плагин хотя бы единожды?(метод с переменными var called = false – не надо рассматривать )
Объясните, пожалуйста, или дайте ссылки по следующему вопросу.
Первый вариант написания плагина учитывает передачу пользователем свойств
Второй вариант учитывает передачу пользователем методов
—>Во втором примере нет $.extend();
Как правильно написать код плагина, чтобы можна было передавать и пользовательские свойства, и методы.
Спасибо за статьи! Очень доходчиво и ясно.
Из интереса чуть дописал Ваш скрипт.
Здесь реализован простой счетчик кликов для изменения цвета текса.
$(“#point”).mySimplePlugin(“yellow”,3);
Добавьте на сайт возможность выделения текста с последующим указанием на ошибку ибо такого количество орфографических и пунктационных ошибок я давно не встречал. Без обид, но глаз режет, тем более, что вы претендуете на звание учащего.
Объясните кто-то, что насчет плагинов вида “$.cookie”. Что если мне не нужно привязываться к DOM? Дайте ссылку по разработке таких плагинов.
Здравствуйте, решил написать плагин уже весь изматерился, не понимаю, почему внутри плагина не сохраняется переменная (this.sec) после присваивания ей значения с ajax запроса
Добавьте
console.log(this)
внутри AJAX обработчика, и посмотрите в консоли результат, почему так – читайте тут http://javascript.ru/basic/closure#oblast-vidimosti-vlozhennoy-funkciiСпасибо за интересную полезную статью. Но у меня возник вопрос: как быть, если нужно иметь в плагине и методы, и настройки для каждого экземпляра плагина индивидуально?
В Вашем примере индивидуальные настройки доступны только в методе init? а как быть, если он мне нужны и в других методах тоже?
Тоже интересен этот вопрос. При вызове плагина еще раз опции не перезаписываются.