Шаблон jQuery-плагина
Обновление от 09.11.2011: Благодаря комментариям оптимизирован код шаблона Обновление от 17.03.2012: Применены новые соглашения по именованию переменных
Решил собрать полный шаблон jQuery-плагина, который используем чаще всего. Решение имеет следующие особенности:
- Есть настройки по умолчанию
- Хранилище настроек и переменных в виде специального объекта
- Поддержка method chaining
- Поддержка public-методов
Шаблон легко дополняется:
- callback'ами
- custom event'ами
Плагин:
/*
Некоторые соглашения:
- Формат имени файла: jquery.<название проекта>.<название плагина>[.min].js (всё в нижнем регистре)
- Формат имён методов и переменных -- camelCase (исключения описаны ниже)
- Переменные, которые ссылаются на jQuery-объекты называются с префиксом "$", например "$someVar"
В следующем блоке комментария идёт стандартный комментарий-заголовок для плагина
*/
/*
Demo plugin from project 'Demo Project'
Dependencies: // Список зависимостей
- jQuery 1.6.4+ - http://jquery.com/
*/
;(function($) { // Стандартная анонимная функция-замыкание, на случай комбинирования с другими скриптами, которые не заканчиваются точкой с запятой перед кодом ставим ";"
/*
Название плагина, см. комментарий ниже, где создаётся jQuery-плагин с таким методом
*/
var jqPluginName = 'demoProject_demoPlugin';
/*
"Класс" плагина
*/
var demoPlugin = function(element, options) {
/*
Инициализация
--------------------------------------------------------------------------------------------------
*/
// В переменной config храним максимум данных, организовываем иерархически как удобно, локальные переменные -- только очень служебные и временные
// Эта переменная у каждого экземпляра плагина своя
var config = $.extend(true, {}, $.fn.demoProject_demoPlugin.defaults, options);
// Эту переменную удобно использовать как контекст для поиска DOM-элементов
config.$context = element;
// Если это необходимо из соображений производительности -- сохраняем ссылки на выбранные DOM-элементы в переменные, используем контекст для поиска элементов
config.ui.$clicker = $(config.ui.clicker, config.$context);
// Кстати:
// if (config.ui.$clicker == null) {...}; // проверка, что была попытка выбрать элемент
// if (config.ui.$clicker.length) {...}; // проверка, что что-то выбралось
/*
Обработчики UI
--------------------------------------------------------------------------------------------------
*/
config.ui.$clicker.click(handleClick);
/*
Private-методы
*/
function handleClick() {
var _this = $(this); // Пример локальной переменной
_this.css({color: 'red'}).fadeOut('slow');
return false;
}
/*
Public-методы
--------------------------------------------------------------------------------------------------
*/
// Для имён public-методов используется UpperCamelCase. this в данном случае -- "класс" плагина
this.Toggle = function() {
config.ui.$clicker.slideToggle();
}
// В data() элемента, к которому был применён плагин, сохраняем ссылку на экземпляр объекта
config.$context.data(jqPluginName, this);
}
/*
Собственно функция-плагин, т.к. добавляется к $.fn должна вызываться на jQuery-объекте ($('#selector').demoProject_demoPlugin(...)),
можно добавлять напрямую к $., тогда можно будет вызывать $.demoProject_demoPlugin(...); но и поддержки chaining'а тогда не будет
Для имён плагинов не получится использовать "неймспейсы, поэтому используется префикс "project_"
http://stackoverflow.com/questions/1219635/jquery-plugin-namespacing-functions
*/
$.fn[jqPluginName] = function(options) {
return this.each(function () {
var _this = $(this);
if (!_this.data(jqPluginName))
{
new demoPlugin(_this, options);
}
});
}
/*
Дефолтовые настройки плагина
*/
$.fn[jqPluginName].defaults = {
$context: null, // Оборачивающий блок, внутри которого работает плагин, удобно использовать в качестве контекста для селекторов
ui: { // Эти секции группируются по смыслу и могут быть произвольной вложенности
$clicker: null, // Ссылка на jQuery-объект
clicker: '.default-clicker', // Селектор для выбора объекта
}
}
})(jQuery); // Будет работать и при jQuery.noConflict();
Тестовый документ:
<!DOCTYPE html> <!-- HTML5, а почему бы и нет? :) -->
<html lang="ru">
<head>
<meta charset="utf-8">
<title>jQuery plugin pattern demo</title>
<script src="js/jquery-1.6.4.min.js"></script>
<script src="js/jquery.demoproject.demoplugin.js"></script>
<script>
$(function(){
// Пример с настройками по умолчанию
$('.context-1').demoProject_demoPlugin();
// Пример с переопределёнными настройками
$('.context-2').demoProject_demoPlugin({
ui: {
clicker: '.custom-clicker'
}
});
// Поддерживается chaining-объектов
$('.context-3').demoProject_demoPlugin().slideUp('slow');
// Пример вызова public-метода для одного экземпляра
$('.api-demo-cliker-single:first').click(function(){
var demoPluginApi = $('.context-1:first').data('demoProject_demoPlugin');
demoPluginApi.Toggle();
return false;
});
// Пример вызова public-метода для нескольких экземпляров
$('.api-demo-clicker:first').click(function(){
$('.context-1').each(function() {
var demoPluginApi = $(this).data('demoProject_demoPlugin');
demoPluginApi.Toggle();
});
return false;
});
});
</script>
</head>
<body>
<h1>Cсылки ниже используют один селектор, но разные экземпляры плагина</h1>
<div class="context-1">
<a href="#" class="default-clicker">Первая ссылка</a>
</div>
<div class="context-1">
<a href="#" class="default-clicker">Первая ссылка</a>
</div>
<div class="context-1">
<a href="#" class="default-clicker">Третья ссылка</a>
</div>
<p><a href="#" class="api-demo-clicker">Клик по этой ссылке вызывает public-метод у нескольких экземпляров</a>, а по этой — <a class="api-demo-cliker-single" href="#">у одного</a></p>
<h1>Ссылка ниже использует другой экземпляр плагина</h1>
<div class="context-2">
<a href="#" class="custom-clicker">Первая ссылка</a>
</div>
<h1>Эта ссылка будет скрыта при загрузке страницы</h1>
<div class="context-3">
<a href="#" class="default-clicker">Первая ссылка</a>
</div>
</body>
</html>
Можно скачать архив примера.
