Ukryty klucz do dynamicznych interakcji w sieci

Kiedy wciąż uczyłem się podstaw tworzenia stron internetowych, jednym z zaskakujących i intrygujących zachowań, jakie napotkałem, było bulgotanie zdarzeń. Na początku wydawało się to niezwykłe, ale kiedy pomyślisz o propagowaniu wydarzeń, zobaczysz, że ma to ogromny sens. Jako twórca stron internetowych z pewnością spotkasz się z bulgotaniem zdarzeń. Czym zatem jest bulgotanie wydarzeń?

Aby zapewnić użytkownikom możliwość interakcji ze stronami internetowymi, JavaScript opiera się na zdarzeniach. Zdarzenie odnosi się do zdarzeń lub działań, które można wykryć i na które można zareagować za pomocą napisanego kodu. Przykładami zdarzeń są między innymi kliknięcia myszą, naciśnięcia klawiszy i przesłanie formularza.

JavaScript używa detektorów zdarzeń do wykrywania zdarzeń i reagowania na nie. Detektor zdarzeń to funkcja, która nasłuchuje lub czeka na wystąpienie określonego zdarzenia na stronie. Na przykład zdarzenie kliknięcia przycisku. Gdy detektor zdarzeń wykryje zdarzenie, którego nasłuchuje, odpowiada, wykonując kod powiązany ze zdarzeniem. Cały proces przechwytywania zdarzeń i reagowania na nie nazywa się obsługą zdarzeń.

Teraz wyobraźmy sobie, że mamy trzy elementy na stronie: element div, zakres i przycisk. Element przycisku jest zagnieżdżony wewnątrz elementu span, a element span jest zagnieżdżony w elemencie div. Ilustrację tego pokazano poniżej:

Zakładając, że każdy z tych elementów ma detektor zdarzeń, który nasłuchuje zdarzenia kliknięcia i po kliknięciu wyświetla na konsoli, co się stanie, gdy klikniesz przycisk?

Aby to przetestować samodzielnie, utwórz folder, a w folderze utwórz plik HTML o nazwie indeks.html, plik CSS o nazwie style.css i plik JavaScript o nazwie app.js.

W pliku HTML dodaj następujący kod:

<!DOCTYPE html>
<html lang="en">

<head>
  <title>Event bubbling</title>
  <link rel="stylesheet" href="https://wilku.top/the-hidden-key-to-dynamic-web-interactions/style.css">
</head>

<body>
  <div>
    <span><button>Click Me!</button></span>
  </div>
  <script src="app.js"></script>
</body>

</html>

W pliku CSS dodaj następujący kod, aby nadać styl elementom div i span.

div {
  border: 2px solid black;
  background-color: orange;
  padding: 30px;
  width: 400px;
}

span {
  display: inline-block;
  background-color: cyan;
  height: 100px;
  width: 200px;
  margin: 10px;
  padding: 20px;
  border: 2px solid black;
}

W pliku JavaScript dodaj następujący kod, który dodaje detektory zdarzeń do elementów div, span i przycisk. Wszystkie listy zdarzeń nasłuchują zdarzenia kliknięcia.

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

const span = document.querySelector('span');
span.addEventListener('click', () => {
  console.log("You've clicked a span element")
})

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button")
})

Teraz otwórz plik HTML w przeglądarce. Sprawdź stronę, a następnie kliknij przycisk na stronie. Co zauważasz? Wynik kliknięcia przycisku pokazano poniżej:

Kliknięcie przycisku uruchamia detektor zdarzeń nasłuchujący zdarzenia kliknięcia przycisku. Jednakże wyzwalane są także detektory zdarzeń na elementach span i div. Dlaczego tak się dzieje, możesz zapytać.

Kliknięcie przycisku uruchamia detektor zdarzeń dołączony do przycisku, który wyświetla dane na konsoli. Ponieważ jednak przycisk jest zagnieżdżony w elemencie span, kliknięcie przycisku oznacza, że ​​technicznie rzecz biorąc, klikamy również element span, w związku z czym uruchamiany jest jego detektor zdarzeń.

Ponieważ element span jest również zagnieżdżony w elemencie div, kliknięcie elementu span oznacza również, że element div również zostanie kliknięty, w wyniku czego zostanie również uruchomiony jego detektor zdarzeń. To jest bulgotanie wydarzeń.

Bulgotanie wydarzenia

Bulgotanie zdarzeń to proces, podczas którego zdarzenie wywołane w zagnieżdżonym zestawie elementów HTML jest propagowane lub rozprzestrzenia się od najbardziej wewnętrznego elementu, w którym zostało wywołane, i przemieszcza się w górę drzewa DOM do elementu głównego, uruchamiając wszystkie detektory zdarzeń nasłuchujące tego zdarzenia.

Detektory zdarzeń są wyzwalane w określonej kolejności, która odpowiada sposobowi rozprzestrzeniania się zdarzenia w drzewie DOM. Rozważmy pokazane poniżej drzewo DOM, które reprezentuje strukturę kodu HTML użytego w tym artykule.

Zdarzenie kliknięcia pojawiające się w drzewie DOM

Drzewo DOM przedstawia przycisk zagnieżdżony wewnątrz zakresu, który jest zagnieżdżony w elemencie div, który jest zagnieżdżony w treści, a treść jest zagnieżdżona w elemencie HTML. Ponieważ elementy zostały zagnieżdżone jeden w drugim po kliknięciu przycisku, zdarzenie click uruchomi detektor zdarzeń dołączony do przycisku.

Jednakże, ponieważ elementy są zagnieżdżone, zdarzenie przesunie się w górę drzewa DOM (bąbelek w górę) do elementu span, następnie do elementu div, następnie do treści i na końcu elementu HTML, uruchamiając wszystkie detektory zdarzeń nasłuchujące w tym przypadku zdarzenia kliknięcia zamówienie.

Dlatego właśnie wykonywany jest detektor zdarzeń dołączony do elementów span i div. Gdybyśmy mieli detektory zdarzeń nasłuchujące kliknięcia treści i elementu HTML, one również zostałyby uruchomione.

Węzeł DOM, w którym zachodzi zdarzenie, nazywany jest celem. W naszym przypadku, ponieważ kliknięcie następuje na przycisk, celem zdarzenia jest element przycisku.

Jak zatrzymać bulgotanie wydarzeń

Aby zapobiec pojawianiu się zdarzenia w DOM, używamy metody o nazwie stopPropagation(), która jest dostępna w obiekcie zdarzenia. Rozważmy poniższy przykładowy kod, którego użyliśmy do dodania detektora zdarzeń do elementu przycisku.

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
})

Kod prowadzi do zdarzenia pojawiającego się w drzewie DOM, gdy użytkownik kliknie przycisk. Aby zapobiec bulgotaniu zdarzeń, wywołujemy metodę stopPropagation(), jak pokazano poniżej:

const button = document.querySelector('button');
button.addEventListener('click', (e) => {
  console.log("You've clicked a button");
  e.stopPropagation();
})

Procedura obsługi zdarzeń to funkcja wykonywana po kliknięciu przycisku. Detektor zdarzeń automatycznie przekazuje obiekt zdarzenia do procedury obsługi zdarzeń. W naszym przypadku ten obiekt zdarzenia jest reprezentowany przez nazwę zmiennej e, która jest przekazywana jako parametr w procedurze obsługi zdarzenia.

Ten obiekt zdarzenia, e, zawiera informacje o zdarzeniu, a także daje nam dostęp do różnych właściwości i metod związanych ze zdarzeniami. Jedną z takich metod jest stopPropagation(), która służy do zapobiegania propagacji zdarzeń. Wywołanie funkcji stopPropagation() w detektorze zdarzeń przycisku zapobiega pojawianiu się zdarzenia w drzewie DOM z elementu przycisku.

Wynik kliknięcia przycisku po dodaniu metody stopPropagation() pokazano poniżej:

Używamy funkcji stopPropagation(), aby zapobiec pojawianiu się zdarzenia z elementu, w którym go używamy. Na przykład, jeśli chcemy, aby zdarzenie kliknięcia przemieszczało się od elementu przycisku do elementu span, a nie przesuwało się dalej w górę drzewa DOM, użylibyśmy funkcji stopPropagation() na detektorze zdarzeń zakresu.

Przechwytywanie zdarzeń

Przechwytywanie zdarzeń jest przeciwieństwem propagowania zdarzeń. Podczas przechwytywania zdarzeń zdarzenie spływa z elementu najbardziej zewnętrznego do elementu docelowego, jak pokazano poniżej:

Zdarzenie kliknięcia spływające do elementu docelowego w wyniku przechwytywania zdarzeń

Na przykład w naszym przypadku, gdy klikniesz element przycisku, podczas przechwytywania zdarzenia, jako pierwsze zostaną uruchomione detektory zdarzeń na elemencie div. Słuchacze w elemencie span podążają i na koniec zostaną uruchomione detektory w elemencie docelowym.

Jednakże propagacja zdarzeń jest domyślnym sposobem propagacji zdarzeń w Document Object Model (DOM). Aby zmienić domyślne zachowanie z propagowania zdarzeń na przechwytywanie zdarzeń, przekazujemy trzeci argument naszym detektorom zdarzeń, aby ustawić przechwytywanie zdarzeń na wartość true. Jeśli nie przekażesz trzeciego argumentu do detektorów zdarzeń, przechwytywanie zdarzeń zostanie ustawione na wartość false.

Rozważmy poniższy detektor zdarzeń:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
})

Ponieważ nie ma trzeciego argumentu, przechwytywanie ma wartość false. Aby ustawić przechwytywanie na wartość true, przekazujemy trzeci argument, wartość logiczną true, która ustawia przechwytywanie na wartość true.

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

Alternatywnie możesz przekazać obiekt, który ustawia przechwytywanie na wartość true, jak pokazano poniżej:

div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, {capture: true})

Aby przetestować przechwytywanie zdarzeń, w pliku JavaScript dodaj trzeci argument do wszystkich detektorów zdarzeń, jak pokazano:

const div = document.querySelector('div');
div.addEventListener('click', () => {
  console.log("You've clicked a div element")
}, true)

const span = document.querySelector('span');
span.addEventListener('click', (e) => {
  console.log("You've clicked a span element")
}, true)

const button = document.querySelector('button');
button.addEventListener('click', () => {
  console.log("You've clicked a button");
}, true)

Teraz otwórz przeglądarkę i kliknij element przycisku. Powinieneś otrzymać taki wynik:

Zauważ, że w przeciwieństwie do bulgotania zdarzeń, gdzie wyjście z przycisku zostało wydrukowane jako pierwsze, w przypadku przechwytywania zdarzeń pierwsze wyjście pochodzi z najbardziej zewnętrznego elementu, czyli elementu div.

Bulgotanie zdarzeń i przechwytywanie zdarzeń to główne sposoby propagacji zdarzeń w DOM. Jednakże propagacja zdarzeń jest powszechnie stosowana do propagowania zdarzeń.

Delegacja Wydarzenia

Delegowanie zdarzeń to wzorzec projektowy, w którym pojedynczy detektor zdarzeń jest dołączony do wspólnego elementu nadrzędnego, na przykład elementu

    , zamiast mieć detektory zdarzeń na każdym elemencie podrzędnym. Zdarzenia elementów podrzędnych następnie przesyłane są do elementu nadrzędnego, gdzie są obsługiwane przez procedurę obsługi zdarzeń w elemencie nadrzędnym.

    Dlatego w przypadku, gdy mamy element nadrzędny z elementami podrzędnymi, do elementu nadrzędnego dodajemy tylko detektor zdarzeń. Ta procedura obsługi zdarzeń obsłuży wszystkie zdarzenia w elementach podrzędnych.

    Być może zastanawiasz się, skąd element nadrzędny będzie wiedział, który element podrzędny został kliknięty. Jak wspomniano wcześniej, detektor zdarzeń przekazuje obiekt zdarzenia do procedury obsługi zdarzeń. Ten obiekt zdarzenia ma metody i właściwości, które oferują informacje o konkretnym zdarzeniu. Jedną z właściwości obiektu zdarzenia jest właściwość target. Właściwość target wskazuje na konkretny element HTML, w którym wystąpiło zdarzenie.

    Na przykład, jeśli mamy nieuporządkowaną listę z elementami listy i do elementu

      dołączymy detektor zdarzeń, gdy zdarzenie wystąpi w elemencie listy, właściwość target w obiekcie event wskaże konkretny element listy, w którym zdarzenie wystąpił.

      Aby zobaczyć delegowanie zdarzeń w akcji, dodaj następujący kod HTML do istniejącego pliku HTML:

      <ul>
          <li>Toyota</li>
          <li>Subaru</li>
          <li>Honda</li>
          <li>Hyundai</li>
          <li>Chevrolet</li>
          <li>Kia</li>
        </ul>

      Dodaj następujący kod JavaScript, aby użyć delegowania zdarzeń w celu użycia pojedynczego detektora zdarzeń w elemencie nadrzędnym do nasłuchiwania zdarzeń w elementach podrzędnych:

      const ul = document.querySelector('ul');
      ul.addEventListener('click', (e) => {
        // target element
        targetElement = e.target
        // log out the content of the target element
        console.log(targetElement.textContent)
      })

      Teraz otwórz przeglądarkę i kliknij dowolny element na liście. Zawartość elementu należy wydrukować na konsolę, jak pokazano poniżej:

      Używając pojedynczego detektora zdarzeń, możemy obsłużyć zdarzenia we wszystkich elementach potomnych. Posiadanie tak wielu detektorów zdarzeń na stronie wpływa na jej wydajność, zużywa więcej pamięci i prowadzi do powolnego ładowania i renderowania stron.

      Delegowanie zdarzeń pozwala nam tego wszystkiego uniknąć, minimalizując liczbę detektorów zdarzeń, których potrzebujemy na stronie. Delegowanie zdarzeń opiera się na propagowaniu zdarzeń. Dlatego możemy powiedzieć, że propagacja zdarzeń może pomóc w optymalizacji wydajności stron internetowych.

      Wskazówki dotyczące skutecznej obsługi zdarzeń

      Jako programista, pracując ze zdarzeniami w modelu obiektowym dokumentu, rozważ użycie delegowania zdarzeń zamiast posiadania wielu detektorów zdarzeń w elementach strony.

      Korzystając z delegowania zdarzeń, pamiętaj o dołączeniu detektora zdarzeń do najbliższego wspólnego przodka elementów podrzędnych, które wymagają obsługi zdarzeń. Pomaga to w optymalizacji propagacji zdarzeń, a także minimalizuje ścieżkę, którą musi pokonać zdarzenie, zanim zostanie obsłużone.

      Podczas obsługi zdarzeń wykorzystaj na swoją korzyść obiekt zdarzenia dostarczony przez detektor zdarzeń. Obiekt zdarzenia zawiera właściwości takie jak target, które są przydatne przy obsłudze zdarzeń.

      Aby mieć bardziej wydajne strony internetowe, unikaj nadmiernej manipulacji DOM. Posiadanie zdarzeń powodujących częste manipulacje DOM może negatywnie wpłynąć na wydajność Twojej witryny.

      Wreszcie, w przypadku elementów zagnieżdżonych, należy zachować szczególną ostrożność podczas dołączania do elementów zagnieżdżonych detektorów zdarzeń. Może to nie tylko wpłynąć na wydajność, ale sprawi, że obsługa zdarzeń będzie bardzo złożona, a kod będzie trudny w utrzymaniu.

      Wniosek

      Zdarzenia są potężnym narzędziem w JavaScript. Bulgotanie zdarzeń, przechwytywanie zdarzeń i delegowanie zdarzeń to ważne narzędzia w obsłudze zdarzeń w JavaScript. Jako programista sieci Web skorzystaj z tego artykułu, aby zapoznać się z koncepcjami umożliwiającymi tworzenie bardziej interaktywnych, dynamicznych i wydajnych witryn internetowych i aplikacji.