Часть 1
В данной статье мы рассмотрим базовые понятия событийной модели StateController'а.
Зоны распространения событий
В селективной модели приложений работа ведется с теми элементами, которые были предварительно выбраны для работы. В чистой событийной модели событие должно распространяться на все элементы DOM-дерева. Это совершенно не важно на маленьких объемах, но при росте количества нод деградация скорости будет даже не линейной. Представьте себе, что событие
click
должно пройтись по всем нодам, чтобы определить, на каких именно элементах оно сработает. Есть предположение, что псевдокласс :hover
в IE6 именно так и работал, поэтому он так сильно тормозил. Чтобы хоть как-то ускорить работу генератора событий, не заставляя его постоянно обходить все дерево нод, лучшим решением является ограничение зон распространения событий. Фактически, мы используем селективную модель внутри событийной, но с некоторыми ограничениями.
Зоны распространения событий позволяют решать еще и другую задачу — можно умело манипулировать результатом, который получается после обхода всех нод. Чтобы лучше это понимать, приведу простой пример.
Например, у вас есть команда программистов, навыки которых вы не знаете. Кто-то умеет хорошо писать на определенных языках, кто-то справляется лучше всех с запросами к базам данных, кто-то отлично создает архитектуры, а кому-то дано быть прирожденным тимлидом. Вам приходит задача написать проект на определенном языке, без использования базы данных. Что вы делаете? Вы идете и генерируете два события.
- Все, кто умеет писать на языке X, шаг вперед
- Сформировать команду из вышедших.
Таким нехитрым способом мы резко сократили зону распространения события во втором пункте до определенных разработчиков.
Приведу еще один пример, который чуть точнее показывает, как можно манипулировать результатом через зоны распространения событий.
Допустим, у вас есть две кнопки: «Сохранить» и «Очистить».
Поведение кнопки «Очистить»
- Очистить все элементы формы (событие
clear_form
) - Скрыть сообщения об ошибках валидации, которые могут остаться от предыдущей отправки данных (событие
clear_form
) - Вернуть динамически-создаваемые элементы формы к исходному виду (событие
clear_form
)
Поведение кнопки «Сохранить»
- Скрыть сообщения об ошибках валидации (событие
clear_form
) - Отправить данные на сервер (событие
submit_form
)
Как видим, у нас есть как минимум одно действие, которое одинаковое для обеих кнопок: скрыть сообщения об ошибках валидации. Но если мы запустим событие
clear_form
на кнопке сохранения без указания зоны распространения события, то мы получим ошибочное поведение — наша форма перед отправкой будет очищена и возвращена к девственно-чистому виду. Нам нужно запускать событие очистки на все элементы формы при нажатии на пнопку «Очистить», и только на элементах сообщений об ошибках, если clear_form
генерируется кнопкой «Сохранить». Ограничения StateController'а
Фреймворк намеренно не использует сложных селекторов, ограничиваясь только следующим списком
- parent
- childNodes
- nodeName
Это сделано намеренно, чтобы держать под контролем количество элементов, которые попадают в зону распространения события. Ограничение сыграло прекрасную службу, заставив более организовано подходить к структурам HTML, заодно сократив количество потенциальных ошибок.
По умолчанию событие распространяется на все элементы внутри контейнера (
container.getElementsByTagName(*)
). Контейнер, с какого начинается запуск события, всегда попадает в зону видимости обработчикам. Можно комбинировать параметры, указывая, к примеру такую зону распространения:
p: "childNodes,A,SPAN"
, что означает — распространять события на контейнер, его дочерние элементы, а также элементы A
и SPAN
внутри этого контейнера. Обработчики
Как я уже писал в первой части, процесс навешивания обработчиков был перенесен прямо в структуру HTML. С точки зрения классической схемы ненавязчивого JS это считается страшным грехом и за это положен как минимум обряд экзорцизма. Однако стоит посмотреть на это решение с другой стороны. При каждой загрузке дополнительного контента (не забываем, что у меня одностраничные приложения), пришлось бы долго и упорно навешивать обработчики на все ноды. И представьте себе, как будет выглядеть ненавязчивый JS-код.
JavaScript часть модуля обычно представляет собой набор сценариев, поэтому туда редко заглядывает разработчик в процессе работы над кодом. Гораздо больше времени проводится при работе с конечными структурами HTML: темплейтированием и описанием логики работы кода. Ну так почему бы туда и не добавить навешивание обработчиков?
StateController использует специальный атрибут
SC
, который содержит перечень обработчиков определенных событий и параметров к ним. Атрибут содержит обычные текстовые директивы, которые парсятся один раз на момент первого запуска события в этой зоне видимости. Повторно обходчик событий работает уже с объектной структурой, благодаря чему достигается примерно пятикратное ускорение по сравнению с первым запуском. Код обработчиков регистриуется отдельно, и в целом процесс похож на добавление плагинов в любом фреймворке.
Запуск и обработка события
После запуска события StateController выбирает некий массив нод, с учетом фильтра зоны распространения. Далее, из этого массива отбираются только те ноды, которые содержать атрибут SC. Среди этих нод выбираются те, у которых есть обработчики, которые реагируют на данное событие. И после этого запускаются обработчики для каждой из оставшихся нод. Стоит помнить, что движение по дереву нод осуществляется от последней ноды к первой.
Параметризация событий
В отличие от стандартной событийной модели браузера, StateController имеет несколько полезных твиков. События могут быть параметризированными, т.е. представляют собой не просто название события, а пару ключ/значение. Это нужно при генерации событий по типу «активирован третий таб», «ошибка №38», «отправка данных блока с контактными данными», «перезагрузка блока со списком пользователей».
Дополнительные данные
С каждым событием могут «путешествовать» дополнительные данные. Это обычный объект-контейнер. Обработчики могут модифицировать поля в этом контейнере по мере выполнения каких-то действий. Зачастую он используется для сбора каких-то данных. К примеру, мы часто используем сбор содержимого полей форм прямо в JSON.
Уровни событий
Дополнительную гибкость дают уровни событий, позволяя создавать локальную зону распространения событий. Например, в примере с кнопкой «Сохранить», описанном выше, можно запустить событие
clear_form
с определенным уровнем запуска, тем самым разрешить разработчику и нодам самим решать, кто какие действия будет выполнять.Документация и примеры
Код фреймворка
Документация
Как это работает
Примеры использования
В следующей статье будет рассмотрен пример использования
Спасибо за внимание!