Розширена робота з об'єктом Event на JavaScript. Введення в спливаючі події Покращені випливаючі події

Головна / Основний функціонал

Починалося все з використання JavaScript та класів.

Однак у мене виникла проблема. Я хотів використати так звані Події, що спливають, але також я хотів мінімізувати залежності, які мені довелося б впроваджувати. Я не хотів підключати бібліотеки jQuery для цього маленького тесту, тільки для того, щоб використовувати спливаючі події.

Давайте подивимося ближче, що таке події, що спливають, як вони працюють, і розглянемо кілька шляхів для їх реалізації.

Окей, то в чому проблема?

Розглянемо простий приклад:

Припустимо, є список кнопок. Щоразу, коли я натискаю на одну з них, вона має стати «активною». Після повторного натискання кнопка має повернутися до початкового стану.

Почнемо з HTML:


Я міг би використовувати стандартний JavaScript обробник подій на кшталт такого:

For(var i = 0; i< buttons.length; i++) { var button = buttons[i]; button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }); }
Виглядає непогано... Але не працюватиме він. Принаймні не так, як ми цього очікуємо.

Замикання перемогли

Для тих, хто трохи знає функціональний JavaScript, проблема є очевидною.

Для інших коротко поясню - функція обробника замикається на змінну button. Однак це змінна одна і перезаписується кожну ітерацію.

У першій ітерації змінна посилається першу кнопку. У наступній – на другу, і так далі. Але, коли користувач натискає кнопку, цикл вже закінчився, і змінна buttonпосилається на останню кнопку, що завжди викликає обробник події для неї. Непорядок.

Що нам потрібно, то це окремий контекст для кожної функції:

Var buttons = document.querySelectorAll(".toolbar button"); var createToolbarButtonHandler = function(button) ( return function() ( if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); );); for(var i = 0; i< buttons.length; i++) { buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i])); }
Набагато краще! А головне – правильно працює. Ми створили функцію createToolbarButtonHandleяка повертає обробник події. Потім кожної кнопки вішаємо свій обробник.

Так в чому проблема?

І виглядає добре, і працює. Незважаючи на це, ми все ще можемо зробити наш код кращим.

По-перше, ми створюємо надто багато обробників. Для кожної кнопки всередині .toolbarми створюємо функцію та прив'язуємо її як обробник події. Для трьох кнопок використання пам'яті незначне.

Але якщо ми маємо щось подібне:

  • // ... ще 997 елементів...

то комп'ютер, звичайно, не вибухне від переповнення. Однак наше використання пам'яті далеке від ідеального. Ми виділяємо її величезну кількість, хоча можна обійтися і без цього. Давайте перепишемо наш код ще раз так, щоб використовувати одну функцію кілька разів.

Замість посилатися на змінну button, щоб стежити, на яку кнопку ми натиснули, ми можемо використовувати eventоб'єкт (об'єкт «події»), який першим аргументом передається до кожного обробника події.

Event об'єкт містить деякі дані про подію. У нашому випадку нас цікавить поле currentTarget. З нього ми отримаємо посилання на елемент, який був натиснутий:

Var toolbarButtonHandler = function(e) ( var button = e.currentTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" );); for(var i = 0; i< buttons.length; i++) { button.addEventListener("click", toolbarButtonHandler); }
Чудово! Ми не тільки спростили все до єдиної функції, яка використовується кілька разів, ми ще й зробили наш код більш читаним, вилучивши зайву функцію-генератор.

Однак ми все ще можемо краще.

Припустимо, ми додали кілька кнопок у аркуш вже після того, як наш код здійснився. Тоді нам також знадобилося б додавати обробники подій для кожної з них. І нам довелося б зберігати посилання на цей обробник та посилання з інших місць. Виглядає не надто привабливо.

Можливо, є й інший підхід?

Почнемо з того, що розберемося, як працюють події і як вони рухаються по нашому DOM.

Як же більшість із них працює?

Коли користувач натискає елемент, генерується подія, щоб сповістити додаток про це. Подорож кожної події відбувається у три стадії:
  1. Фаза перехоплення
  2. Подія виникає для цільового елемента
  3. Фаза спливання
Примітка: не всі події проходять стадію перехоплення або спливу, деякі створюються відразу на елементі. Однак це скоріше виняток із правил.

Подія створюється зовні документа і потім послідовно переміщається по ієрархії DOM до target(цільового) елемента. Як тільки воно дісталося своєї мети, подія тим самим шляхом вибирається з DOM елемента.

Ось наш HTML шаблон:


Коли користувач натискає кнопку А, подія подорожує таким чином:

початок
| #document
| Фаза перехоплення
| HTML
| BODY
| UL
| LI#li_1
| Кнопка А < - Событие возникает для целевого элемента
| Фаза спливання
| LI#li_1
| UL
| BODY
| HTML
v #document

Зауважте, що ми можемо простежити шлях, яким подія рухається до свого цільового елемента. У нашому випадку для кожної натиснутої кнопки ми можемо бути впевнені, що подія випливе назад, пройшовши через свого батька. ulелемент. Ми можемо використовувати це та реалізувати спливаючі події.

Спливаючі події

Випливаючі події - це ті події, які прив'язані до елемента батька, але виконуються лише у випадку, якщо вони задовольняють будь-яку умову.

Як конкретний приклад візьмемо нашу панель інструментів:

Ul class="toolbar">


  • Тепер, знаючи, що будь-яке натискання на кнопці спливе через елемент ul.toolbarдавайте прикріпимо наш обробник подій на нього. На щастя, він уже має:

    Var toolbar = document.querySelector(".toolbar"); toolbar.addEventListener("click", function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. remove("active"); ));
    Тепер ми маємо набагато чистіший код, і ми навіть позбулися циклів! Зауважте, що ми замінили e.currentTargetна e.target. Причина у тому, що ми обробляємо події іншому рівні.

    e.target- фактична мета події, те, куди вона пробирається через DOM, і звідки потім спливатиме.
    e.currentTarget– поточний елемент, який обробляє подію. У нашому випадку це ul.toolbar.

    Поліпшені спливаючі події

    В даний момент ми обробляємо будь-яке натискання на кожен елемент, який спливає через ul.toolbar, але наша умова перевірки надто проста. Що сталося б, якби мали більш складний DOM, що включає іконки та елементи, які не були створені для того, щоб по них кликали?


    Упс! Тепер, коли ми клацаємо на li.separatorабо значок, ми додаємо йому клас .active. Як мінімум, це недобре. Нам потрібен спосіб фільтрувати події так, щоб ми реагували на потрібний елемент.

    Створимо для цього невелику функцію-помічника:

    Var delegate = function(criteria, listener) ( return function(e) ( var el = e.target; do ( if (!criteria(el)) continue); return; ) while((el = el.parentNode)); ); );
    Наш помічник робить дві речі. По-перше, він обходить кожен елемент та його батьків і перевірять, чи задовольняють вони умові, переданій у параметрі criteria. Якщо елемент задовольняє - помічник додає об'єкту події поле, що називається delegateTarget, В якому зберігається елемент, що задовольняє нашим умовам. І потім викликає оброблювач. Відповідно, якщо жоден елемент не задовольняє умову, жодного оброблювача не буде викликано.

    Ми можемо використовувати це так:

    Var toolbar = document.querySelector(".toolbar"); var buttonsFilter = function(elem) ( return elem.classList && elem.classList.contains("btn"); ); var buttonHandler = function(e) ( var button = e.delegateTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" );); toolbar.addEventListener("click", delegate(buttonsFilter, buttonHandler));
    Те, що лікар прописав: один обробник подій, прикріплений до одного елемента, який робить всю роботу. Але робить її лише для необхідних нам елементів. І він чудово реагує на додавання та видалення об'єктів з DOM.

    Підбиваючи підсумки

    Ми коротко розглянули основи реалізації делегування (обробки спливаючих) подій на чистий JavaScript. Це добре тим, що нам не потрібно генерувати та прикріплювати купу обробників для кожного елемента.

    Якби я хотів зробити з цього бібліотеку або використовувати код у розробці, я додав би пару речей:

    Функція-помічник для перевірки задоволення об'єкта критеріїв у більш уніфікованому та функціональному вигляді. Начебто:

    Var criteria = ( isElement: function(e) ( return e instanceof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return criteria.isElement(e) && e.classList.contains(cls); ) ) // Більше критеріїв);
    Часткове використання помічника так само було б не зайвим:

    Var partialDelgate = function(criteria) ( return function(handler) ( return delgate(criteria, handler); ) );
    Оригінал статті: Understanding Delegated JavaScript Events
    (Від перекладача: мій перший, судіть суворо.)

    Щасливого кодингу!

    На цьому уроці ми познайомимося з таким поняттям як сплив події, а також розглянемо, як його можна перервати. Крім цього з'ясуємо, які ще етапи (фази) проходить подія, перш ніж почати виринати.

    Випливання події

    Якщо деякий елемент виникає подія, воно починає " спливати " , тобто. виникає у батька, потім у прабатька і т.д.

    З цього випливає, що подію, яку згенерував певний елемент, можна перехопити за допомогою оброблювача на батька, прабатька і т.д.

    Сплив події (бульбашка) продемонструємо на наступному прикладі:

    Заголовок

    Деякий дуже важливийтекст

    Розділ

    Деякий текст

    Решта тексту

    Напишемо невеликий скрипт, за допомогою якого додамо обробник події "click" для всіх елементів сторінки, а також для об'єктів document і window.

    Створимо HTML-сторінку і вставимо в неї наведений вище HTML код. Сценарій, написаний на мовою JavaScript, вставимо перед тегом body , що закриває . Після цього відкриємо щойно створену сторінку у веб-браузері, натисніть клавішу F12 і перейдемо в консоль. Тепер натиснемо лівою кнопкою мишкою в області, що належить елементу strong, і подивимося, як подія спливатиме.

    Як перервати сплив події

    Сплив події (бульбашка) можна перервати. У цьому випадку у вищестоящих (батьківських) елементів дана подія викликана не буде. Метод, який призначений для припинення спливання події (бульбашка) називається stopPropagation() .

    Наприклад, змінимо наш наведений вище приклад таким чином, щоб подія не спливала вище body:


    Click on this link

    Як видно, ми не вказуємо програми обробки подій у тезі . Натомість ми пишемо

    window.captureEvents(Event.CLICK);

    для того, щоб перехопити подію Clickоб'єктом window. Зазвичай об'єкт window не працює з подією Click. Однак, перехопивши, потім його переадресуємо в об'єкт window. Зауважимо, що в Event.CLICKфрагмент CLICKповинен писатися великими літерами. Якщо ж Ви хочете перехоплювати кілька подій, Вам слід відокремити їх один від одного символами |. Наприклад:

    window.captureEvents(Event.CLICK | Event.MOVE);

    Крім цього у функції handle(), призначеної нами на роль обробника подій, ми користуємося інструкцією return true;. Насправді це означає, що браузер повинен обробити і саме посилання після того, як завершиться виконання функції handle(). Якщо ж Ви напишете натомість return false;, то на цьому все закінчиться.

    Якщо тепер у тезі Ви поставите програму обробки події onClick, то зрозумієте, що ця програма у разі виникнення цієї події викликана не буде. І це не дивно, оскільки об'єкт window перехоплює сигнал про подію ще до того, як він досягає об'єкта link. Якщо ж Ви визначите функцію handle()як

    function handle(e) (
    alert("The window object captured this event!");
    window.routeEvent(e);
    return true;
    }

    то комп'ютер перевірятиме, чи визначено інші програми обробки подій для даного об'єкта. Змінна e - це наш об'єкт Event, що передається функції обробки подій як аргумент.

    Крім того, Ви можете безпосередньо надіслати сигнал про подію будь-якому об'єкту. Для цього Ви можете скористатися методом handleEvent(). Це виглядає так:



    "Клікніть" за цим посиланням

    onClick="alert("Обробник подій для другого посилання!");">Друге посилання

    Усі сигнали про подіях Click, посилаються на обробку за другим посиланням - навіть якщо Ви зовсім і не клацнули по жодному з посилань!

    Наступний скрипт показує, як скрипт може реагувати на сигнали про натискання клавіш. Натисніть будь-яку клавішу і подивіться, як працює цей скрипт.


    © 2022 androidas.ru - Все про Android