- PVSM.RU - https://www.pvsm.ru -

Использование событий в jQuery плагинах

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

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

1. Теория
1.А. Традиционный плагин jQuery
1.Б. Область видимости переменных
1.В. Работа с событиями в jQuery
1.Г. Применение событий в плагинах
1.Д. Недостатки событий
2. Пример простого приложения с дроблением на плагины
3. Заключение, вывод
4. Список литературы

1. Теория

1.А. Традиционный плагин jQuery

Плагин в jQuery — это обертка над элементом, реализующая некоторый функционал (поведение), которое обычно ограничено элементом на который навешен плагин.

Плагином может быть практически любой элемент или группа элементов на странице: slider изображений, диалоговое окно, кнопка, видоизмененный checkbox, видоизмененный select, динамический список элементов, и так далее.

Создание традиционного jQuery плагина задача достаточно тривиальная. Для этого необходимо добавить в пространство $.fn новый метод.

Пример 1 (создание плагина myPlugin):

(function( $ ) {
  $.fn.myPlugin = function(options) {
  
	// Переменная options - настройки плагина
	// Объект $(this) - доступ к объекту jQuery с выбранным элементом, на который навешен плагин

	// Код плагина будет располагаться здесь

  };
})( jQuery );

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

Но часто нам необходимо получить данные или состояние плагина. Хорошим примером является DatePicker.

Мне известно несколько решений для получения данных:

  • Р.1.А. Вызов метода (или функции).
    Пример 2: $('input.date').datePicker('getDate'); $('input.date').datePicker('isDisabled').
    Недостаток: нет возможности отследить изменения (например, пользователь выбрал другое значение);
  • Р.1.Б. Передача callback-функции.
    Пример 3: $('input.date').datePicker({onChange: onChangeCallback}); $('input.date').datePicker({onDisable: onDisableCallback}).
    Недостаток: Усложнение реализации плагина: необходимо реализовать вызов callback-функции, подписку на событие нескольких слушателей;
  • Р.1.В. События.
    Пример 4: $('input.date').on('change', onChangeHandler); $('input.date').on('disable', onDisableHandler).
    Подробней о достоинствах и недостатках дальше.
1.Б. Область видимости переменных

Проблема в реализации методов Р.1.А и Р.1.Б заключается в том, что при следующем вызове $(element).datePicker(...) повторно выполняется «конструктор» плагина. Конечно это легко отслеживается путем проверки передаваемых параметров: если передан объект (опции) — это инициализация; если передана строка — необходим вызов метода. Как говорится, «баг это или фича» — пока не хватает опыта понять.

Но я столкнулся с проблемой областей видимости переменных.

Пример 5: Демонстрация проблемы областей видимости [1]

(function( $ ) {  
  // Опции
  var options = {
    date: '00-00-0000'
  },
  // Методы
  methods = {
    getDate: function()
    {
      return options.date;
    }
  };
  
  // Плагин
  $.fn.myPlugin = function(o)
  {
    if (typeof o === 'string' && methods[o] !== null) {
        return methods[o].apply();
    }
    
    options = o || options;
    
    $(this).text(options.date);
    
  };
  
  // Инициализация плагина
  $('[data-test]').myPlugin({
    date: '10-10-2013'
  });
  
  // Получение данных
  alert($('[data-test]').myPlugin('getDate'));
  
})( jQuery );

На данном этапе все просто: создаем плагин, который выводит дату из опций.

Но проблема возникает, когда необходимо поместить на страницу больше одного плагина данного типа.

Пример 6: Больше одного плагина на странице [2]

Как видим из примера 6 — переменная options, которая была вынесена за пределы плагина — перезаписалась данными при инициализации плагина 2.

Решения для примера 6:

  • Р.2.A. В объекте options хранить данные для инициализации всех плагинов (усложнение кода);
  • Р.2.Б. Крепить данные инициализации к DOM-объекту (с моей точки зрения: неплохое решение);
  • Р.2.В. Использование событий. Вызов методов при помощи $(...).trigger(...). В конце я приведу пример, который лишен данного недостатка.
1.В. Работа с событиями в jQuery

При помощи библиотеки jQuery мы можем как создавать «слушателей» для событий $('.link').on('click', onClickHandler), так и вызывать (провоцировать) события $('.link').trigger('click').

Обратимся к документации, посмотрим параметры обеих функций:

В общих чертах функция on [3] имеет параметры:

  • events — список событий которые необходимо слушать;
  • handler — функция-обработчик события, которая будет вызвана при совершении любого события из списка events.

Функция trigger [4] имеет параметры:

  • eventType — название события, которое необходимо вызвать;
  • extraParameters — массив или объект, содержащий параметры, которые необходимо передать обработчику события.

Пример 7: Демонстрирующий работу событий [5]

$(function() {
  
  $(document).on('myEvent', function(event, p) {
    alert('a: ' + p.a + '; b: ' + p.b);
  });
  
  $(document).trigger('myEvent', {
    a: '1',
    b: '2'
  });
  
});
1.Г. Применение событий в плагинах

Чтобы избавиться от проблемы с областью видимости переменных (описанной в примере П.6) и проблемой с навешиванием нескольких слушателей (описанной в решении Р.1.Б) я прибегнул к использованию событий и оберток, реализованных в jQuery (1.В).

Пример 7: Реализация событий в плагине [6]

(function( $ ) {
  
  // Объявление плагина
  $.fn.datePicker = function(options)
  {
  
	var self = $(this),
        options = options || {
          year: 2012,
          month: 1,
          day: 1
        };

    var date = new Date(options.year, options.month, options.day),
        d = new Date(options.year, options.month, 1),
        table = $('<table />'),
        tr = $('<tr>');
    
    tr.appendTo(table);
    
    // Добавляем начало месяца
    for (i=0; i < d.getDay(); i++) {
      tr.append('<td />');
    }
    
    // Добавляем даты
    while (d.getMonth() == date.getMonth()) {
      if (i % 7 == 0) {
        tr = $('<tr />');
        tr.appendTo(table);
        i = 0;
      }
      
      var td = $('<td>' + d.getDate() + '</td>');
      td.data('date', d.toString());
      tr.append(td);
      
      d.setDate(d.getDate() + 1);
      i++;
    }
    
    // Добавляем конец месяца
    for (i=0; i < 7 - d.getDay(); i++) {
      tr.append('<td />');
    }
   
    table.appendTo(self);
    
    // При клике на ячейку таблицы генерируем событие 'change' для плагина
    table.find('td').on('click', function() {
      // Вторым параметром передаем дату
      self.trigger('change', $(this).data('date'));
    });
    
    return self;
    
  };
  
  
  // Инициализация плагина
  $('[data-datePiceker]').datePicker();
  
  
  // Навешивание слушателя на событие
  $('[data-datePiceker]').on('change', function(event, date) {
    alert(date);
  });
  
})( jQuery );

Что мы получили:

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

Пример 8 Демонстрирующий работу нескольких инициализаций одного плагина П.7 [7]

1.Д. Недостатки событий

Конечно данный подход меняет привычный подход к работе с плагином, я имею ввиду отсутствие традиционных методов получения данных, например $(...).datePicker('getDate'), но тем не менее на данном этапе моего личного развития, как программиста, данный способ решает некоторые мои проблемы. Я лично, считаю данный поход довольно универсальным, и с недостатками пока не сталкивался — если читатель знает о таковых, пожалуйста не молчите, и хочу так же попросить писать как можно доступней и адекватней.

2. Пример простого приложения с дроблением на плагины

Для тренировки и обкатки событий написал несколько маленьких Ajax-приложений:

3. Вывод, заключение

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

Я не затронул темы всплывания событий по дереву DOM, а так же хотелось уделить больше внимания созданию приложений с использованием событий. Но, если на то будет карма — обязательно напишу продолжение.

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

Спасибо за внимание!

4. Список литературы

  1. twitter.github.com/flight/ [10]
  2. api.jquery.com/trigger/ [4]
  3. api.jquery.com/on/ [3]
  4. docs.jquery.com/Plugins/Authoring [11]

Автор: jMas

Источник [12]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/javascript/28260

Ссылки в тексте:

[1] Пример 5: Демонстрация проблемы областей видимости: http://jsbin.com/upuyiy/1/edit

[2] Пример 6: Больше одного плагина на странице: http://jsbin.com/upuyiy/2/edit

[3] on: http://api.jquery.com/on/

[4] trigger: http://api.jquery.com/trigger/

[5] Пример 7: Демонстрирующий работу событий: http://jsbin.com/izecug/1/edit

[6] Пример 7: Реализация событий в плагине: http://jsbin.com/eguyif/2/edit

[7] Пример 8 Демонстрирующий работу нескольких инициализаций одного плагина П.7: http://jsbin.com/eguyif/4/edit

[8] jsbin.com/afupej/1/edit: http://jsbin.com/afupej/1/edit

[9] jsbin.com/urihar/16/edit: http://jsbin.com/urihar/16/edit

[10] twitter.github.com/flight/: http://twitter.github.com/flight/

[11] docs.jquery.com/Plugins/Authoring: http://docs.jquery.com/Plugins/Authoring

[12] Источник: http://habrahabr.ru/post/170987/