Здравствуйте.
Хочу поделиться с вами одной техникой организации кода при массивной работе с DOM-элементами. Несколько лет назад, когда еще вовсе не было бэкбона и MVVC, мы писали старый добрый джаваскрипт без фреймворков: создавали объекты и заставляли их плясать на странице в общем танце. Такая практика, без сомнения, до сих пор оправдывает себя, и техника, о которой пойдет речь, применима именно к ней.
Мой рассказ — о маленькой библиотечке PageObject.js (текущая версия v0.14, 2.6K) и о том, как с ее помощью можно упростить себе жизнь.
Это не плагин jQuery, хотя, уверен, многие захотят ее назвать именно так. Это всего лишь функция, которая использует селекторы и кое-какие утилиты из jQuery, чтобы создать удобство в чтении и написании кода. По сути, это простая jQuery утилитка.
Суть техники
В создании объектов, которые манипулируют DOM-элементами, и их подключении на страницу ничего нового не придумаешь: создаем конструктор, сперва рендерим в нем определенный шаблон, затем разбираем его результат на части, «вешаем» на эти части обработчики событий, программируем остальную логику и встраиваем все это добро в наше приложение.
function Calculator() {
if (this.constructor.name !== 'Calculator') { throw “No way, buddy!”; }
// 1. отрендерить шаблон
// 2. разобрать на части
// 3. повесить обработчики
// 4. остальная логика
}
PageObject.js помогает с первыми двумя шагами.
function Calculator() {
if (this.constructor.name !== 'Calculator') { throw “No way, buddy!”; }
var calc = this;
$.turnToPageObject(calc, {
template: $('#tmplCalculator').html(),
containerClass: 'calc',
context: {
caption: "Calculator"
},
selectors: {
buttons: [ ':button', Calculator.getButtonName ],
led: 'p'
}
});
// 3. повесить обработчики
// 4. другая логика
}
var calc = new Calculator;
$('body').append(calc.DOM.container);
После того, как отработает функция $.turnToPageObject
(“превратить в объект страницы”), у объекта calc
появится свойство calc.DOM
— неймспейс, заполненый DOM-элементами, которые будут соответствовать указанным селекторам, и еще появится calc.DOM.container
— именно та легко встраиваемая в приложение часть объекта — контейнер всего-всего.
Вот, пожалуйста, полный работающий пример с калькулятором. Мне же осталось рассказать подробнее о всех возможностях утилитки.
$.turnToPageObject
Первый аргумент должен быть объектом, у которого в последствии появится заполненый составными HTML-елементами неймспейс DOM
. Второй аргумент — опции.
Если НЕ указать опцию container
, то контейнер будет создан (без него никак), и это будет такой же элемент, как и containerElement
(по умолчанию — DIV
).
Если указать containerClass
, то контейнеру будет присвоен класс.
Если указать template
, то будет отрендерен шаблон и его результат будет помещен внутрь контейнера.
Если НЕ указать context
, то шаблон будет отрендерен с пустым контекстом {}.
template
может быть либо строкой, либо функцией (интеграция с Jammit JST).
Когда template
— функция, он принимает только контекст и должен возвратить строку.
Когда template
— строка, шаблон рендерится при помощи templateEngine
, которая автоматически настроится по умолчанию на использование шаблонизатора _.template
, если underscore присутствует.
Если в вашем проекте нет underscore, вам нужно сконфигурировать templateEngine
.
// вот так меняются умолчания:
$.turnToPageObject.configure({
templateEngine: window.tmpl, // http://ejohn.org/blog/javascript-micro-templating
containerElement: 'strong'
});
templateEngine
принимает два аргумента — строку шаблона и контекст — и также должен возвратить строку.
Если указана опция hide
, то контейнер будет спрятан, что часто бывало удобно.
И главное — селекторы. Если указать опцию selectors
(объект), то соответствующие селекторам найденые в содержимом контейнера HTML-элементы будут по аналогичным ключам помещены в неймспейс DOM
. Если опция template
была указана, то селекторы будут искать в уже отрендеренном шаблоне.
Если по селектору не будет найдено ни одного элемента — будет крик.
Если по селектору будет найдено более одного элемента — также будет исключение.
Если все же нужно, чтобы по селектору были найдены и помещены в массив более одного элемента, нужно к значению селектора дописать [] (пустые квадратные скобки) — такое выделение себя оправдывает.
Если нужно, чтобы множественные найденные елементы были помещены в объект (как в примере с калькулятором), значение селектора нужно записать в виде массива из двух элементов: первый элемент — собственно селектор, а второй — функция, которая из каждого найденого по указанному селектору элемента должна извлечь ключ (напр. айдишник) для помещения этого элемента в соответствующем неймспейсе.
Стоит отметить, что дополнительные неймспейсы в селекторах образуют неймспейсы с теми же именами внутри свойства DOM
.
Пожалуй, на этом все.
Лушее понимание того, что и как работает, вы сможете получить, почитав тесты и заглянув в исходник на гитхабе. Наверняка также будет полезной и общая документация.
Буду рад, если вы найдете данную технику и описанную выше конвенцию применимой и в ваших проектах. Особенно буду рад вашим пожеланиям и идеям, с радостью отвечу на вопросы.
Автор: mcmlxxxiii