Если вы много использовали jQuery, то вы, вероятно, уже знакомы с привязкой событий. Это довольно простой материал, но копайте немного глубже, и вы найдете возможности сделать свой управляемый событиями код менее хрупким и более управляемым.
Лучшая стратегия выбора
Давайте начнем с основного примера. Вот HTML-код для меню навигации, которое может быть включено или выключено:
<button class="nav-menu-toggle">Toggle Nav Menu</button> <nav> <ul> <li><a href="/">West Philadelphia</a></li> <li><a href="/cab">Cab Whistling</a></li> <li><a href="/throne">Throne Sitting</a></li> </ul> </nav>
А вот немного JavaScript для переключения навигационного меню при нажатии кнопки:
$('.nav-menu-toggle').on('click', function() { $('nav').toggle(); });
Это, наверное, самый распространенный подход. Это работает, но это хрупко. JavaScript зависит от элемента button, имеющего класс nav-menu-toggle
. В будущем другому разработчику или даже забывчивому вам будет очень просто не понять этого и удалить или переименовать класс во время рефакторинга.
Суть проблемы в том, что мы используем классы CSS как для презентации, так и для взаимодействия. Это нарушает принцип разделения интересов , делая обслуживание более подверженным ошибкам.
Давайте попробуем другой подход:
<button data-hook="nav-menu-toggle">Toggle Nav Menu</button> <nav data-hook="nav-menu"> <ul> <li><a href="/">West Philadelphia</a></li> <li><a href="/cab">Cab Whistling</a></li> <li><a href="/throne">Throne Sitting</a></li> </ul> </nav>
На этот раз мы используем атрибут data-hook
( data-hook
) для идентификации элементов. Любые изменения, касающиеся CSS-классов, больше не будут влиять на JavaScript, предоставляя нам лучшее разделение задач и более прочный код.
Нам просто нужно обновить селекторы jQuery, чтобы вместо них использовать data-hook
:
$('[data-hook="nav-menu-toggle"]').on('click', function() { $('[data-hook="nav-menu"]').toggle(); });
Заметьте, что я решил использовать data-hook
для элемента nav
. Вам не нужно, но мне нравится понимание, которое он предоставляет: всякий раз, когда вы видите data-hook
, вы знаете, что на элемент ссылаются в JavaScript.
Немного синтаксического сахара
Я признаю, что селекторы data-hook
данных не самые красивые. Давайте исправим это, расширив jQuery пользовательской функцией:
$.extend({ hook: function(hookName) { var selector; if(!hookName || hookName === '*') { // select all data-hooks selector = '[data-hook]'; } else { // select specific data-hook selector = '[data-hook~="' + hookName + '"]'; } return $(selector); } });
Имея это в виду, мы можем переписать JavaScript:
$.hook('nav-menu-toggle').on('click', function() { $.hook('nav-menu').toggle(); });
Намного лучше. Мы можем даже иметь список разделенных пробелами имен ловушек на элементе:
<button data-hook="nav-menu-toggle video-pause click-track">Toggle Nav Menu</button>
И найдите любое имя крюка в:
$.hook('click-track'); // returns the button as expected
Мы также можем выбрать все элементы хука на странице:
// both are equivalent $.hook(); $.hook('*');
Избегайте выражений анонимных функций
До сих пор в примерах в качестве обработчика событий использовалось выражение анонимной функции. Давайте перепишем код для использования объявленной функции:
function toggleNavMenu() { $.hook('nav-menu').toggle(); } $.hook('nav-menu-toggle').on('click', toggleNavMenu);
Это делает строку кода, которая делает привязку события намного проще для чтения. Имя функции toggleNavMenu
передает намерение и является хорошим примером самодокументируемого кода.
Мы также получаем возможность многократного использования, так как другие области кода также могут использовать toggleNavMenu
в случае необходимости.
Наконец, это большой выигрыш для автоматизированного тестирования, поскольку объявленные функции гораздо проще для модульного тестирования, чем выражения для анонимных функций.
Работа с несколькими событиями
JQuery предлагает удобный синтаксис для обработки нескольких событий. Например, вы можете указать разделенный пробелами список событий, которые будут обрабатываться одним обработчиком событий:
$.hook('nav-menu-toggle').on('click keydown mouseenter', trackAction);
Если вам нужно обрабатывать несколько событий с разными обработчиками событий, вы можете использовать обозначение объекта:
$.hook('nav-menu-toggle').on({ 'click': trackClick, 'keydown': trackKeyDown, 'mouseenter': trackMouseEnter });
С другой стороны, вы также можете отменить привязку нескольких событий одновременно:
// unbinds keydown and mouseenter $.hook('nav-menu-toggle').off('keydown mouseenter'); // nuclear option: unbinds everything $.hook('nav-menu-toggle').off();
Как вы можете себе представить, небрежное освобождение может привести к огромным нежелательным побочным эффектам. Продолжайте читать для методов, чтобы смягчить это.
Отвязка с осторожностью
Нет ничего необычного в том, чтобы связать несколько обработчиков событий для одного и того же события в элементе. Давайте вернемся к этой кнопке ранее:
<button data-hook="nav-menu-toggle video-pause click-track">Toggle Nav Menu</button>
Различные области кода могут быть заинтересованы в том, что происходит при нажатии кнопки:
// somewhere in the nav code $.hook('nav-menu-toggle').on('click', toggleNavMenu); // somewhere in the video playback code $.hook('video-pause').on('click', pauseCarltonDanceVideo); // somewhere in the analytics code $.hook('click-track').on('click', trackClick);
Независимо от различных используемых селекторов, кнопка теперь имеет три обработчика события щелчка. Теперь представьте, что наш аналитический код завершен, заботясь о кнопке:
// no good $.hook('click-track').off('click');
К сожалению, фактически удаляются все обработчики событий щелчка, а не только trackClick
. Мы должны быть более проницательными и указать конкретный обработчик событий, который нужно удалить:
$.hook('click-track').off('click', trackClick);
Другой вариант — использовать пространства имен. Любое событие может быть квалифицировано с пространством имен во время привязки или отмены привязки, предоставляя вам более полный контроль:
// binds a click event in the "analytics" namespace $.hook('click-track').on('click.analytics', trackClick); // unbinds only click events in the "analytics" namespace $.hook('click-track').off('click.analytics');
Вы даже можете использовать несколько пространств имен:
// binds a click event in both the "analytics" and "usability" namespaces $.hook('click-track').on('click.analytics.usability', trackClick); // unbinds any events in either the "analytics" OR "usability" namespaces $.hook('click-track').off('.usability .analytics'); // unbinds any events in both the "analytics" AND "usability" namespaces $.hook('click-track').off('.usability.analytics');
Обратите внимание, что порядок пространств имен не имеет значения. Пространства имен не являются иерархическими.
Если у вас есть сложная функциональность, которая требует связывания различных событий между несколькими элементами, тогда пространства имен — это простой способ сгруппировать их для быстрой очистки:
// free all elements on the page of any "analytics" event handling $('*').off('.analytics');
Пространства имен особенно полезны при написании плагинов, так как вы можете быть уверены, что ваш плагин является хорошим гражданином, который освобождает только свои собственные обработчики событий.
Прощальные слова
Привязка событий jQuery хороша тем, что ее легко запустить, но она предоставляет множество функций, когда это необходимо. Надеюсь, я поделился одним или двумя трюками, которые помогут вам написать управляемый событиями JavaScript, который будет более надежным, понятным и более управляемым.
Спасибо за прочтение!