Михаил Давыдов Разработчик JavaScript
JavaScript События
Паттерн PubSub
4
PubSub
• Издатель (Publisher) – Генерирует данные одного типа – Издает только одну газету – Издателей может быть много
• Подписчики (Subscribers) – Подписываются на данные издателя – Могут отписаться в любой момент
• Данные – поток – Поздно подписался – упустил данные – Объем и частота публикаций задается издателем
5
var PubSub = function () { this.readers = []; }; PubSub.prototype = { pub: function (data) {}, sub: function (callback) {}, unsub: function (callback) {} }; // function callback(data) {}
PubSub – пример
6
PubSub – особенности
• Самая простая реализация • Только 1 тип данных
– А хочется еще и "Мурзилку" получать
• Нужно напрямую общаться с объектом
Event Emitter
8
Event Emitter
• Имеет много названий • Издатель
– Генерирует данные разных типов – Издает газеты и журналы – Издателей может быть много
• Подписчики – Подписываются на данные любого типа в любом количестве – Могут отписаться в любой момент
• Данные – поток – Поздно подписался – упустил данные – Объем и частота публикаций задается издателем
9
var EventEmitter = function () { // events = {"event_name": []}; this.events = {}; }; EventEmitter.prototype = { emit: function (event, data) {}, on: function (event, callback) {}, off: function (event, callback) {} }; // function callback(data) {}
Event Emitter – пример
10
Event Emitter – особенности
• Много разных типов данных • Нужно иметь ссылку на EventEmitter
11
Event Manager
12
Event Manager
• Имеет много названий • Своеобразная шина данных • Единственный издатель
– Генерирует данные разных типов
• Подписчики – Подписываются на данные любого типа в любом количестве – Могут отписаться в любой момент
• Данные – поток – Поздно подписался – упустил данные – Объем и частота публикаций задается издателем
13
Похож на Event Emitter, но это Синглентон
var EventManager = { events: {}, emit: function (event, data) {}, on: function (event, callback) {}, off: function (event, callback) {} }; // function callback(data) {}
Event Manager – пример
14
Event Manager – особенности
• Много разных типов данных • Об этом объекте знают все
Дополнительные фичи
Управление событиями, namespace
Накопление данных
События на дереве DOM
Делегирование событий
Фильтрация событий
Их наличие зависит от конкретны задач
Фичи направлены на управление группами событий и агрегацией
18
Управление событиями
• Группировка событий – namespace
• Легкое удаление событий – Удаление события без callback – Удаление по дескриптору
19
jQuery 1.8
var handler = function () {}; $('body').on('click.ns', handler); $('body').off('click', handler); // не удобно $('body').off('click'); // все click $('body').off('.ns'); // весь namespace $('body').off(); // все события $('body').off('**'); // все делегированные
Управление событиями – пример
20
Накопление данных
• Опоздал с подпиской – ничего не получил – Это не удобно
• Пользователь желает получить все – Всю подписку журналов с нуля
• Как только подписался – сразу получил • Похоже на Promise
21
jQuery DOMReady
$(function () { $(function () { console.log('Tada!'); }); });
Накопление данных – пример
22
jQuery Ajax#done
var data = $.get('/'); data.done(function () { data.done(function () { console.log('Tada!'); }); });
Накопление данных – пример
DOM события
События на DOM дереве
Event bubbling
Event capturing
Не всплывающие события
24
Event bubbling
• Событие начинается с текущего элемента • Поднимается по DOM дереву • Последний элемент – корень дерева
– window или document
25
<div>
Event bubbling
<div> <div>
*Click*
26
<div>
Event bubbling
<div> <div>
*Click*
27
<div>
Event bubbling
<div> <div>
*Click*
28
<div>
Event bubbling
<div> <div>
*Click*
29
Event bubbling – фаза по умолчанию в jQuery
// jQuery 1.7 .on() $('.b-form-button').on('click', function (event) { console.log(this); // .b-form-button element }); // Хэлперы $('.b-form-button').click(function (event) { console.log(event); });
Event bubbling в jQuery
http://api.jquery.com/on/ http://api.jquery.com/category/events/
http://api.jquery.com/category/events/event-object/
30
Функция bindTo()
BEM.DOM.decl('b-form-input', { onSetMod : { 'js' : { 'inited' : function () { this .bindTo(this.elem('input'), { 'focus' : this.onFocus, 'blur' : this.onBlur }); } } } });
Event bubbling в i-bem
http://bem.github.com/bem-bl/sets/common-desktop/i-bem/i-bem.ru.html
31
Не всплывающие события
• var itBubbles = event.bubbles;!• Специфичные события для 1 элемента
– submit, focus, blur, load, unload, change, reset, scroll
• Некоторые события мутаций DOM – DOMFocusIn, DOMFocusOut, DOMNodeRemoved
https://developer.mozilla.org/en-US/docs/Mozilla_event_reference
32
Особенности
• Фазы Capturing & Bubbling идут одновременно – Чередуются capturing, bubbling
• Каждый обработчик получает новый инстанс объекта Event
http://jsfiddle.net/h2nJU/2/
Делегирование
Один обработчик – несколько целей
Основа – всплытие событий
34
<ul> onclick=delegateClick
Делегирование
<li class="good"> *Click* <li class="good">
<li class="bad">
35
<ul> onclick=delegateClick
Делегирование – начало события
<li class="good"> *Click* <li class="good">
<li class="bad">
36
<ul> onclick=delegateClick
Делегирование – всплытие события
<li class="good"> *Click* <li class="good">
<li class="bad">
37
var isTarget = event.target.classList .contains('good');
38
<ul> onclick=delegateClick
Делегирование
<li class="good">
<li class="good">
<li class="bad"> *Click*
39
<ul> onclick=delegateClick
Делегирование – начало события
<li class="good">
<li class="good">
<li class="bad"> *Click*
40
<ul> onclick=delegateClick
Делегирование – всплытие события
<li class="good">
<li class="good">
<li class="bad"> *Click*
41
Событие не произойдет
contains('good') === false;
42
Профит делегирования
• Меньше обработчиков событий – Экономия памяти – Меньше утечек памяти
• Проще работать с динамическими элементами
43
Особенности делегирования
• Если событие не всплывает – ничего не получится
• Нужно максимально ограничивать событие – Необходимо выбрать наиболее близкого делегата
• Возможны частые ложные вызовы события – Большие затраты на вызов функции
• Ограничить делегирование у частых событий – mousemove
• Не использовать делегирование если элемент 1
44
// jQuery 1.7+ .on() $('.b-container') .on('click', '.b-item', function (event) { console.log(this); // .b-item }); // jQuery 1.3+ $('.b-item') .live('click', function (event) { console.log(this); // .b-item }); // === $(document).on('click', '.b-item')
Делегирование в jQuery
http://api.jquery.com/on/ http://api.jquery.com/live/
Управление DOM событием
Прерывание всплытия
Прерывание действия по умолчанию
46
Прерывание всплытия
• event.stopPropagation() – Прерывает всплытие события по дереву
• event.stopImmediatePropagation() – Прерывает всплытие события по дереву – Отменяет прочие обработчики
http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Event-stopPropagation
47
48
Прерывание действия по умолчанию
• Действие по умолчанию – Переход по ссылке – Раскрытие <select> – Сабмит формы – Фокус в инпут – Снятие фокуса
• event.preventDefault() – Перерывает это действие
• Некоторые действия прервать нельзя – var isCanPrevent = event.cancelable; – resize, scroll, focus, error, load, unload, DOM*, …
https://developer.mozilla.org/en-US/docs/DOM/event.preventDefault
49
$('.b-link').click('click', function (event) { return false; // preventDefault+stopPropagation }); $('.b-form').click('submit', function (event) { return isValid(this); // this - .b-form }); $('.b-link').click('click', function (event) { event.preventDefault(); event.stopPropagation(); });
Прерывание событий jQuery
http://api.jquery.com/on/ http://api.jquery.com/live/
debounce, throttle
Фильтрация событий
http://benalman.com/projects/jquery-throttle-debounce-plugin/
Бывает, что событий очень много, а обрабатывать каждое дорого
52
throttle
• Фильтрация частоты сообщений • Событие вызывается не чаще чем раз в N миллисекунд
53
$.throttle(timeout, callback, no_trailing);
54
$.throttle
no_trailing=false
no_trailing=true
Пауза
Пауза Поток событий
Допущенные события
55
var log = $.throttle(250, function () { console.log(arguments); }); $(window).scroll(log); $(window).off('scroll', log);
$.throttle
http://benalman.com/code/projects/jquery-throttle-debounce/examples/throttle/
56
debounce
• Склеивает вызовы в один • Событие вызывается только после паузы в
N милисекунд
57
$.debounce(timeout, callback, at_begin);
58
$.debounce
at_begin=false
at_begin=true
Пауза
Пауза
59
function suggest(event) {}; $('input').keyup(suggest); $('input').keyup($.debounce(250, suggest)); $('input').unbind('keyup', suggest);
$.debounce
http://benalman.com/code/projects/jquery-throttle-debounce/examples/debounce/
Неуместное использование Накладные расходы
Проблемы событий
События это круто – давайте везде использовать!!1!
62
Вариант не удачного использования событий
DataObject.on('someData', function (data) { console.log(data); }); DataObject.trigger('giveMe:someData');
Событие геттер
63 DataObject.someData
Y U NO
64
События – поток. Их лучше использовать как поток данных
65
Module A Module B
66
Module A Module B
Data C
67
Module A Module B
Data C
Give me C
68
Module A Module B
Data C
Data C'
Give me C
69
Module A Module B
Data C
Data C'
Give me C
70
Module A Module B
Data C Data C
71
Накладные расходы
• Событие – функция – n объектов, k ресурсов = n*k функций – функция занимает ресурсы – лишние скоупы, сборка мусора и JIT-компиляция – вызов кучи функций – утечки памяти
Events everywhere!
73
Не блокирующий I/O
• Дефрагментация времени блокировок – Более плотная загрузка процесса
• Выгодно применять, когда время I/O больше времени обработки данных
• Меньше проблем с разделяемой памятью
74
Слабое связывание
• Элементы не знают о программе • Элементы общаются только событиями • Меньше разделенной памяти
– Меньше проблем синхронизации
75
trigger(message)! on(message, callback)!
76
Заключение
• События – PubSub – Event Emitter – Event Manager
• Фичи событий – namespace – накопление данных
• DOM события – Event Bubbling – Делегирование – Прерывание события – Отмена действия по умолчанию
• Фильтрация
Top Related