,
Są te same handlery co poprzednio, ale tym razem – na etapie immersji. Cóż, aby zobaczyć przechwycenie w akcji, kliknij element w nim
Programy obsługi będą działały w kolejności odgórnej: FORM → DIV → P.
Kod JS wygląda tak:
varelems = document.querySelectorAll("formularz,dział,p"); // na każdym elemencie zawiesimy handler na etapie przechwytywania for (var i = 0; i< elems.length; i++) {
elems[i].addEventListener("click", highlightThis, true);
}
Nikt ci nie przeszkadza, aby przypisać obsługę do obu etapów, w ten sposób:
varelems = document.querySelectorAll("formularz,dział,p"); dla (zmienna i = 0; i< elems.length; i++) {
elems[i].addEventListener("click", highlightThis, true);
elems[i].addEventListener("click", highlightThis, false);
}
Kliknij element wewnętrzny
Aby zobaczyć kolejność przekazywania wydarzenia: Powinien być FORM → DIV → P → P → DIV → FORM. Zauważ, że element
Weźmie udział w obu etapach.
Wyniki
Po wystąpieniu zdarzenia element, na którym zdarzenie miało miejsce, jest oznaczony jako event.target.
Zdarzenie najpierw przemieszcza się w dół z katalogu głównego dokumentu do event.target , wywołując po drodze procedury obsługi dostarczone przez addEventListener(…., true).
Zdarzenie przemieszcza się od event.target do początku dokumentu, po drodze wywołuje procedury obsługi dostarczone przez addEventListener(…., false).
Każdy handler będzie miał dostęp do właściwości zdarzenia:
event.target to najgłębszy element, na którym zdarzenie faktycznie miało miejsce.
event.currentTarget (=this) to element, na którym ten moment samoobsługa pracowała (do czego „doszła” impreza).
event.eventPhase - w jakiej fazie uruchomiono procedurę obsługi zdarzenia (dive = 1, float = 3).
Propagowanie można zatrzymać, wywołując metodę event.stopPropagation(), ale nie jest to zalecane, ponieważ może być potrzebne zdarzenie do nieoczekiwanych celów.
Teraz przyjrzymy się niektórym zaawansowanym rzeczom podczas pracy z obiektem Event, a mianowicie: propagacji i przechwytywaniu, a także delegowaniu zdarzeń.
Bąbelkowanie zdarzeń
Wyobraź sobie, że masz kilka zagnieżdżonych bloków:
najbardziej wewnętrzny blok
Kiedy klikniesz na najbardziej wewnętrzny blok, zdarzenie na kliknięcie występuje najpierw w nim, a następnie uruchamia się w jego rodzicu, w rodzicu swojego rodzica i tak dalej, aż osiągnie ten tag ciało i na metce html (następnie do dokument a wcześniej okno ).
I to jest logiczne, ponieważ klikając na blok wewnętrzny, jednocześnie klikasz wszystkie zewnętrzne.
Zobaczmy to na poniższym przykładzie: mamy 3 bloki, każdy z nich ma dołączone zdarzenie onclick:
Kliknij najbardziej wewnętrzny czerwony klocek - a zobaczysz, jak zadziała najpierw kliknięcie czerwonego klocka, potem niebieskiego, a następnie zielonego:
To zachowanie nazywa się wynurzenie zdarzenia - przez analogię do wznoszenia się pęcherzyka powietrza od dołu. Podobnie jak bańka, nasze kliknięcie na wewnętrzny element wydaje się unosić do góry, za każdym razem uruchamiając się na wyższych blokach.
event.cel
Powiedzmy, że mamy dwa elementy: div i paragraf p, które znajdują się wewnątrz tego div. Połączmy onlicka z divą:
Kiedy klikniemy na ten div, możemy przejść do akapitu, lub możemy przejść do miejsca, w którym ten akapit nie istnieje.
Jak to możliwe - spójrz na następujący przykład: zielony to nasz div, a niebieski to nasz akapit:
Jeśli klikniesz na zieloną część, klikniemy na div, a jeśli klikniesz na niebieską część, kliknięcie nastąpi najpierw na akapicie, a następnie na div. Ale ponieważ onclick jest dołączony konkretnie do div, na ogół możemy nie zauważyć obecności akapitu.
Czasami jednak chcielibyśmy wiedzieć, czy kliknięcie nastąpiło bezpośrednio na div, czy na jego akapicie podrzędnym. Pomoże nam w tym obiekt Event i jego właściwość. event.cel - przechowuje dokładnie ten element, w którym nastąpiło kliknięcie.
W poniższym przykładzie mamy div , w środku leży p , a w nim - Zakres .
Powiążmy zdarzenie onclick z najwyższym elementem (div) i kliknijmy różne elementy: div, p, span. Używając event.cel pobierz najniższy element, w którym wydarzyło się zdarzenie, i wyświetl jego nazwę za pomocą tagName .
Jeśli klikniesz np. na span, to zdarzenie złapie naszego div (przecież onclick jest do niego podpięty), ale w event.cel skłamie dokładnie Zakres :
Kliknij na różne bloki - zobaczysz wynik:
Zaprzestanie wynurzania
Więc już wiesz, że wszystkie wydarzenia pojawiają się na samym szczycie (do tag html a następnie do dokumentu, a następnie do okna). Czasami istnieje potrzeba przerwania tego wynurzania. Może to zrobić dowolny element, przez który pojawia się zdarzenie. Aby to zrobić, w kodzie elementu wywołaj metodę event.stopPropagacja() .
W poniższym przykładzie kliknięcie na czerwony klocek zadziała samo na siebie, potem na niebieski klocek i to wszystko - niebieski klocek zatrzyma dalsze wznoszenie, a zielony klocek nie zareaguje w żaden sposób:
Kliknij czerwony blok - zobaczysz wynik:
Zanurzenie
Oprócz bulgotania wydarzeń jest też nurkować (według naukowym etap przechwytywania ). Oznacza to, że zdarzenie najpierw przechodzi od góry do dołu (etap przechwytywania), dociera do naszego elementu (etap celu), a dopiero potem zaczyna się unosić (etap bąbelkowania).
Możesz zawiesić obsługę zdarzeń biorąc pod uwagę etap przechwycenia tylko przy pomocy addEventListener . Aby to zrobić, ma trzeci parametr: jeśli jest równa true, zdarzenie zostanie uruchomione na etapie przechwytywania, a jeśli false, na etapie propagacji (domyślnie):
Vargreen = document.getElementById("zielony"); green.addEventListener("klik", func, prawda); funkcja(zdarzenie) ( )
Etap, na którym doszło do zdarzenia, można określić za pomocą właściwości event.eventPhase . Może przyjmować następujące wartości: 1 – etap przechwycenia, 2 – etap docelowy, 3 – etap wynurzania.
Wprowadzenie do delegacji
Wyobraź sobie sytuację: pozwól nam mieć ul Z kilkoma Li . Do każdego li dołączane jest następujące zdarzenie: po kliknięciu li na jego końcu dodawany jest znak „!”.
Zaimplementujmy powyższe:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
var li = document.querySelectorAll("#ul li"); //W pętli zawieszamy funkcję addSign na każdym li: for (var i = 0; i
Kliknij na li - zobaczysz, jak "!" jest dodawane na ich końcu:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Niech teraz mamy też przycisk, po kliknięciu którego dodawany jest nowy na końcu ul Li z tekstem „pozycja”. Czeka nas niespodzianka: załączone wydarzenie nie zadziała dla nowych ja! Upewnijmy się, że:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li
Kliknij przycisk, aby dodać li, a następnie ten nowy li - nie zareaguje:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li
Aby rozwiązać problem, w momencie tworzenia nowego li zawieś na nim funkcję addSign przez addEventListener. Zaimplementujmy to:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li var li = document.querySelectorAll("#ul li"); dla (zmienna i = 0; i
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li
Jest drugi sposób obejścia problemu - delegowanie wydarzeń. Przeanalizujmy to.
Delegacja wydarzeń
Istota delegacji jest następująca: zawiesimy wydarzenie nie na każdym li, ale na ich rodzicu - na ul .
Jednocześnie należy zachować wydajność naszego skryptu: tak jak poprzednio, po kliknięciu li na jego końcu zostanie dodany znak „!”. Tylko wydarzenie w nowej odsłonie zawiśnie na ul:
var ul = document.getElementById("ul"); //Zawieś wydarzenie na ul: ul.addEventListener("klik", addSign); funkcja addSign() ( )
Jak to zrobić: skoro zdarzenie jest zawieszone na ul, wewnątrz funkcji możemy złapać li event.cel . Przypomnę, czym jest event.target - to jest dokładnie ten tag, w którym nastąpiło kliknięcie, w naszym przypadku tak jest Li .
Oto rozwiązanie naszego problemu poprzez delegację:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Wynik wykonania kodu:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
W takim przypadku nasze rozwiązanie zadziała automatycznie nawet dla nowego li , bo impreza wisi nie na li, a na ul:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li var ul = document.getElementById("ul"); ul.addEventListener("kliknij", addSign); function addSign() ( event.target.innerHTML = event.target.innerHTML + "!"; ) //Implementacja przycisku dodawania nowego li: var button = document.getElementById("button"); button.addEventListener("klik", addLi); funkcja addLi() ( var li = document.createElement("li"); li.innerHTML = "new li"; ul.appendChild(li); )
Kliknij na przycisk, aby dodać li, a następnie na to nowe li - zareaguje:
paragraf 1
punkt 2
punkt 3
punkt 4
punkt 5
Dodaj li
Nasz kod działa, ale nie bez wad. Przeanalizujmy te niedociągnięcia i napiszmy bardziej uniwersalne rozwiązanie.
Ogólne delegowanie zdarzeń
Wada naszego kodu objawi się, gdy wewnątrz li znajdzie się kilka zagnieżdżonych tagów. W naszym przypadku niech to będą tagi i :
W takim przypadku naciśnięcie i spowoduje dodanie wykrzyknika do koniec tagu , nie tag Li , jak byśmy sobie życzyli (jeśli klikniesz li poza kursywą, to wszystko będzie ok):
ustęp kursywa 1
ustęp kursywa 2
ustęp kursywa 3
ustęp kursywa 4
ustęp kursywa 5
var ul = document.getElementById("ul"); ul.addEventListener("kliknij", addSign); funkcja addSign() ( event.target.innerHTML = event.target.innerHTML + "!"; )
Kliknij kursywę - zobaczysz, jak „!” zostanie dodany na końcu (naciśnięcie poza kursywą zadziała):
Problem jest rozwiązany w następujący sposób (opisana metoda nie jest jedyna, ale najprostsza): używając najbliższej metody znajdujemy najbliższe li, które jest rodzicem event.target w następujący sposób: event.target.closest("li") .
Jak to działa: jeśli kliknięcie było włączone i , potem w event.cel to kłamię i w event.target.closest("li") - nasze li, za które impreza powinna odpalić.
Jeśli kliknięcie było na Li , potem w event.cel , i w event.target.closest("li") nasze li będzie kłamać.
Sprawdźmy:
ustęp kursywa 1
ustęp kursywa 2
ustęp kursywa 3
ustęp kursywa 4
ustęp kursywa 5
var ul = document.getElementById("ul"); ul.addEventListener("klik", function(event) ( var li = event.target.closest("li"); if (li) ( //sprawdź, czy w ogóle nie ma li nadrzędnego li.innerHTML = li.innerHTML + "!";) ));
Wynik wykonania kodu:
Bez względu na głębokość zagnieżdżenia: tag i może być w tagu b i ten w tagu Zakres i dopiero wtedy w Li - nieważne: budowa event.target.closest("li") znajdzie rodzica z dowolnego poziomu zagnieżdżenia.
Gdy wystąpi zdarzenie, programy obsługi uruchamiają się najpierw na samym zagnieżdżonym elemencie, następnie na jego rodzicu, a następnie powyżej i tak dalej, w górę łańcucha zagnieżdżania.
Na przykład są 3 zagnieżdżone elementy FORM > DIV > P , z obsługą na każdym:
Kod:
Bąbelkowanie zapewnia, że kliknięcie na wewnętrznej stronie
Wywoła obsługę onclick (jeśli istnieje) najpierw na
Dlatego jeśli klikniesz na P w powyższym przykładzie, to alert będzie wyświetlany kolejno: p → div → forma.
Ten proces nazywa się bulgotaniem, ponieważ wydarzenia bulgoczące od elementu wewnętrznego w górę przez rodziców, podobnie jak pęcherzyk powietrza unoszą się w wodzie.
event.cel
Niezależnie od tego, na jakim elemencie złapiemy wydarzenie, zawsze możesz dowiedzieć się dokładnie, gdzie się ono wydarzyło. Najgłębszy element, który uruchamia zdarzenie, nazywa się elementem „target” lub „source” i jest dostępny jako event.target.
Różnice w stosunku do tego (=event.currentTarget):
event.target jest elementem źródłowym, na którym wystąpiło zdarzenie, nie zmienia się podczas procesu propagacji.
jest to bieżący element, do którego dotarło bąbelkowanie, na którym aktualnie wykonuje procedurę obsługi.
Na przykład, jeśli istnieje tylko jeden moduł obsługi form.onclick, to „łapie” wszystkie kliknięcia wewnątrz formularza. Wszędzie tam, gdzie w środku jest kliknięcie - wyskoczy do elementu
Div (margin-dolny: 10px; )
Teraz trochę JavaScript - tutaj zaimplementujemy bardzo proste sprawdzenie wewnątrz procedury obsługi zdarzenia onsubmit (zdarzenie submit jest uruchamiane w formularzu, gdy jest on przesyłany), który sprawdza, czy pola tekstowe są puste. Jeśli tak, wywołujemy funkcję PreventDefault() obiektu zdarzenia — która zatrzymuje przesyłanie formularza — a następnie wyświetlamy komunikat o błędzie w akapicie poniżej naszego formularza, aby poinformować użytkownika, co jest nie tak:
Const form = document.querySelector("formularz"); const fname = document.getElementById("fname"); const lname = document.getElementById("lname"); const para = document.querySelector("p"); form.onsubmit = function(e) ( if (fname.value === "" || lname.value === "") ( e.preventDefault(); para.textContent = "Musisz wpisać obie nazwy! "; ) )
Oczywiście jest to dość słaba walidacja formularza - na przykład nie zatrzymałaby użytkownika walidacji formularza za pomocą spacji lub liczb wprowadzonych w pola - ale jest OK na przykład dla celów.Wyjście jest następujące:
Propagowanie i przechwytywanie zdarzeń
Ostatnim tematem do omówienia jest coś, z czym nie będziesz się często spotykać, ale może to być prawdziwy ból, jeśli go nie rozumiesz. Propagowanie i przechwytywanie zdarzeń to dwa mechanizmy opisujące, co się dzieje, gdy dwa programy obsługi tego samego typu zdarzenia są aktywowane na jednym elemencie. Spójrzmy na przykład, aby to ułatwić - otwórz przykład show-video-box.html w nowej karcie (iw innej karcie). Jest on również dostępny na żywo poniżej:
Ukryty przykład wideo
Pokaż przykład skrzynki wideo
Wyświetl wideo
zamiast tego link do filmu.
Jest to dość prosty przykład, który pokazuje i ukrywa a ) jest ogólnym kontenerem zawartości przepływu. Nie ma wpływu na treść ani układ, dopóki nie zostanie stylizowany za pomocą CSS.">
z ) osadza odtwarzacz multimedialny, który obsługuje odtwarzanie wideo w dokumencie. Możesz użyć
również dla treści audio, ale element może zapewnić bardziej odpowiednie wrażenia użytkownika."> element w nim:
Wyświetl wideo
Twoja przeglądarka nie obsługuje wideo HTML5. Zamiast tego jest link do filmu.
Notatka : Po co zawracać sobie głowę zarówno przechwytywaniem, jak i bulgotaniem? Cóż, w dawnych, złych czasach, kiedy przeglądarki były znacznie mniej kompatybilne niż obecnie, Netscape używał tylko przechwytywania zdarzeń, a Internet Explorer tylko propagacji zdarzeń. Kiedy W3C zdecydowało się spróbować ujednolicić zachowanie i dojść do konsensusu, skończyło się na tym systemie, który zawierał oba, czyli tym, który zaimplementowano w nowoczesnych przeglądarkach.
Notatka : Jak wspomniano powyżej, domyślnie wszystkie programy obsługi zdarzeń są rejestrowane w fazie propagacji, co w większości przypadków ma większy sens. Jeśli naprawdę chcesz zamiast tego zarejestrować zdarzenie w fazie przechwytywania, możesz to zrobić, rejestrując swoją procedurę obsługi za pomocą addEventListener() i ustawiając opcjonalną trzecią właściwość na true .
delegacja wydarzenia
Bąbelkowanie pozwala nam również skorzystać z delegacja wydarzenia - ta koncepcja opiera się na fakcie, że jeśli chcesz, aby jakiś kod uruchamiał się po kliknięciu dowolnego z wielu elementów podrzędnych, możesz ustawić nasłuchiwanie zdarzeń na jego rodzicu i sprawić, że zdarzenia, które się na nim zachodzą, będą bąbelkować do ich rodzica zamiast nastawiać słuchacza zdarzeń na każde dziecko z osobna. Pamiętasz, jak powiedzieliśmy wcześniej, że propagacja obejmuje sprawdzenie elementu, na którym zdarzenie jest uruchamiane, najpierw pod kątem obsługi zdarzeń, a następnie przejście do rodzica elementu itp.?
Dobrym przykładem jest seria elementów listy - jeśli chcesz, aby każdy z nich wyskakiwał komunikat po kliknięciu, możesz ustawić detektor zdarzenia kliknięcia na nadrzędnym
, a wydarzenia będą bąbelkować z elementów listy do .
Ta koncepcja jest dalej wyjaśniona w blogu Davida Walsha, z wieloma przykładami — zobacz Jak działa delegowanie zdarzeń JavaScript .
Wniosek
Powinieneś teraz wiedzieć wszystko, co musisz wiedzieć o wydarzeniach internetowych na tym wczesnym etapie. Jak wspomniano powyżej, zdarzenia nie są tak naprawdę częścią podstawowego kodu JavaScript — są zdefiniowane w interfejsach Web API przeglądarki.