Интересной особенностью jQuery UI для меня стала обработка событий и запуск функций обратного вызова. У обычных событий цепочки функций-обработчиков выполняются от начала и до конца или пока не будут прерваны через метод stopImmediatePropagation. К сожалению, в отличие от YUI 3 , в API jQuery не предоставлена возможность определить, когда закончится выполняться эта цепочка. Зато в jQuery UI виджеты генерируют события, в которых такая возможность есть.
При конфигурировании виджета можно задать функцию обратного вызова (callback) для любого события, а так же подписаться на это событие извне. По сути, callback и цепочка обработчиков вызываются одним и тем же приватным методом _trigger . Но callback всегда вызывается последним вне зависимости от результата работы цепочки функций. А так как им обоим передается ссылка на одно и тот же событие, то в callback можно проверить статус события после обработчиков через методы isDefaultPrevented, isPropagationStopped или isImmediatePropagationStopped.
Более того, метод _trigger возвращает значение true, если все функции были выполнены и ни одна из них не вызвала preventDefault. В противном случае он вернет значение false. Это свойство фабрики виджетов тоже можно использовать для управления логикой работы событий.
Простой пример виджета:
$.widget("my.input", {
_init: function () {
var me = this;
// подписываемся на некоторые события элемента input,
// чтобы отслеживать его изменение
me.element.find(".input").bind('keyup change blur', function (event) {
var value = $(this).val(), opts = me.options, data;
// исколючаем повторяющие события
if (!opts.inProgress && value != opts.value) {
try {
opts.inProgress = true;
data = {'current': value, 'last': opts.value};
me._change(data, me._trigger("change", event, data));
} finally {
opts.inProgress = false;
}
}
});
},
_change: function (data, status) {
$(this.element).toggleClass("error", !status);
this.options.value = data.current;
}
});
$.extend($.my.input, {
defaults: {
value: "",
inProgress: false
}
});
$(function () {
$("#demo")
.input({'change': function (e, data) {
// функция обратного вызова
// будет выполнена самой последней при обработке события
return data.current <= 1000;
}})
.bind('inputchange', function (e, data) {
// специальный обработчик, который прерывает цепочку событий,
// если встречается определенная строка — 007
if (data.current == "007") {
e.stopImmediatePropagation();
return false;
}
})
.bind('inputchange', function (e, data) {
// еще один специальный обработчик
$(".log", this).prepend("<p>" + data.current + "</p>");
});
});
Этот виджет отслеживает вводимые в поле данные, ведет лог этих данных и осуществляет валидацию. В примере я использую метод stopImmediatePropagation , чтобы прервать выполнение цепочки. Если это специально не требуется, то лучше использовать preventDefault или возвращать false в обработчике.
Итак, если в поле ввести «007», то в лог ничего не запишется, а виджету будет назначен класс «error». Так же этот класс будет назначен, если значение в поле — число, больше 1000.