Пишем свой widget

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

Первое, о чём стоит рассказать, это то, что правила написания плагинов для jQuery слишком вальяжны, что не способствует их качеству. При создании jQuery UI, походу, решили пойти путём стандартизации процесса написания плагинов и виджетов. Я не могу сказать насколько задумка удалась, но стало явно лучше чем было. Начну с описания каркаса для вашего виджета:

$.widget("book.expose", {
  // настройки по умолчанию
  options: {
    color: "red"
  },
  // инициализация widget
  // вносим изменения в DOM и вешаем обработчики
  _create: function() {
    this.element;    // искомый объект в jQuery-обёртке
    this.name;       // имя - expose
    this.namespace;  // пространство – book
    this.element.on("click." + this.eventNamespace, function(){
      console.log("click");
    });
  },
  // метод отвечает за применение настроек
  _setOption: function( key, value ) {
      // применяем изменения настроек
      this._super("_setOption", key, value );
  },
  // метод _destroy должен быть антиподом к _create
  // он должен убрать все изменения, внесенные в DOM, и убрать все обработчики, если таковые были
  _destroy: function() {
    this.element.off('.'+this.eventNamespace);
  }
});

Поясню для тех, кто не прочёл комментарии:

options

хранилище настроек виджета для конкретного элемента

_create()

отвечает за инициализацию виджета – тут в DOM должны происходить изменения и «вешаться» обработчики событий

_destroy()

антипод для _create() – должен подчистить всё, что мы намусорили

_setOption(key, value)

данный метод будет вызван при попытке изменить какие-либо настройки:

$("#my").expose({key:value})

Наблюдательный глаз заметит, что все перечисленные методы начинаются со знака подчёркивания – это такой способ выделить «приватные» методы, которые недоступны для запуска. Если мы попытаемся запустить $('#my').expose('_destroy'), то получим ошибку. Но учтите – это лишь договорённость, соблюдайте, её!

Для обхода договорённости о приватности можно использовать метод data():

$("#my").data("expose")._destroy() // место для смайла «(evil)»

В данном примере я постарался задать хороший тон написания виджетов – я «повесил» обработчики событий в namespace. Это даст в дальнейшем возможность контролировать происходящее без необходимости лазить в код виджета. «True story».

Код, описанный в методе _destroy(), избыточен, т.к. он и так выполняется в публичном destroy(). Приведён тут для наглядности.

А для ленивых, чтобы не прописывать каждый раз eventNamespace в обработчиках событий, разработчики добавили в версии 1.9.0 два метода: _on() и _off(). Первый принимает два параметра:

  • DOM-элемент, или селектор, или jQuery-объект

  • набор обработчиков событий в виде объекта

Все перечисленные события будут «висеть» в пространстве eventNamespace, т.е. результат будет предположительно одинаковым:

this._on(this.element, {
  mouseover:function(event) {
    console.log("Hello mouse");
  },
  mouseout:function(event) {
    console.log("Bye mouse");
  }
});

Второй метод, _off(), позволяет выборочно отключать обработчики:

this._off(this.element, "mouseout click");

Ну, каркас баркасом, пора переходить к функционалу. Добавим произвольную функцию с произвольным функционалом:

callMe:function() {
  console.log("Allo?");
}

К данной функции мы легко сможем обращаться как из других методов виджета, так и извне:

// изнутри
this.callMe()
// извне
$("#my").expose("callMe")

Если ваша функция принимает параметры, то передача оных осуществляется следующим способом:

$("#my").expose("callMe", "Hello!")

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

{
  _create: function() {
    var self = this; // вот он!
    this.element.on("click."+this.eventNamespace, function() {
      // тут используем self, т.к. this уже указывает на
      // элемент по которому кликаем
      self.callMe();
    })
  }
}

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

// инициируем событие
this._trigger("incomingCall");

// подписываемся на событие при инициализации виджета
$("#my").expose({
  incommingCall: function(ev) {
    console.log("din-don");
  }
})

// или после, используя в качестве имени события
// имя виджета + имя события
$("#my").on("exposeincomingCall", function() {
  console.log("tru-lya-lya")
});

Материала много, я понимаю, но ещё добавлю описание нескольких методов, которые можно вызвать из самого виджета:

_delay() – данная функция работает как setTimeout(), вот только контекст переданной функции будет указывать на сам виджет (это чтобы не заморачиваться с областью видимости)

_hoverable() и _focusable() – данным методам необходимо скармливать элементы, для которых необходимо отслеживать события hover и focus, чтобы автоматически добавить к ним классы ui-state-hover и ui-state-focus при наступлении оных

_hide() и _show() – эти два метода появились в версии 1.9.0, они созданы дабы стандартизировать поведение виджетов при использовании методов анимации; настройки принято прятать в опциях под ключами «hide» и «show» соответственно. Использовать методы следует следующим образом:

{
  options: {
    hide: {
      effect: "slideDown", // настройки эквиваленты вызову
      duration: 500        // .slideDown( 500)
    }
  }
}
// внутри виджета следует использовать вызовы _hide() и _show()
this._hide( this.element, this.options.hide, function() {
  // это наша функция обратного вызова
  console.log('спрятали');
});

Существует ещё пара методов, которые реализованы до нас:

{
  enable: function() {
    return this._setOption( "disabled", false );
  },
  disable: function() {
    return this._setOption( "disabled", true );
  }
}

Фактически, данные функции создают синоним для вызова:

$("#my").expose({ "disabled": true }) // или false

Наша задача сводится лишь к отслеживанию этого флага в методе _setOption().

Возможно, этот виджет и не будет популярен, зато он наглядно демонстрирует, как создавать виджеты для jQuery UI:

Будьте внимательны, с выходом jQuery UI версии 1.9.0 были внесены правки в Widget API, следовательно, большинство доступной информации устарело, так что читайте официальную документацию. А ещё лучше – заглядывайте в код готовых виджетов «от производителя».

Информация по теме разработки виджетов:

Last updated