Zdarzenia#

Mysz#

Moduł zdarzeń myszy # (mouse event module) jest rozwinięciem modułu zdarzeń interfejsu użytkownika. Definiuje on interfejs MouseEvent oraz powiązane ze wskazującymi urządzeniami wejściowymi (np. myszą lub trackballem) następujące typy zdarzeń:

Implementacje muszą podtrzymywać aktualny licznik kliknięć # (current click count) podczas generowania zdarzeń myszy. Musi to być nieujemna liczba całkowita wskazująca liczbę kolejnych kliknięć przycisku urządzenia wskazującego w określonym czasie. Opóźnienie po którym następuje wyzerowanie licznika jest specyficzne dla konfiguracji środowiska. Liczba kliknięć będzie udostępniana pod właściwością UIEvent.detail.

W przypadku elementów zagnieżdżonych wymienione typy zdarzeń są zawsze kierowane do najbardziej zagnieżdżonego elementu. Przodkowie docelowego elementu mogą skorzystać z bąbelkowania aby uzyskać powiadomienia dla zdarzeń myszy, które wystąpiły na którymś z ich elementów potomnych. Jest to szczególnie użyteczne przy delegowaniu zdarzeń.

Kolejność zdarzeń myszy#

Zdarzenia myszy występują w ustalonej kolejności względem siebie. Oto typowa sekwencja zdarzeń, która musi wystąpić, kiedy kursor urządzenia wskazującego jest przenoszony nad element A:

Nazwa zdarzeniaElementUwagi
1.mousemove
Urządzenie wskazujące jest przenoszone nad element A...
2.mouseoverA
3.mouseenterA
4.mousemoveAWielokrotne zdarzenia
Urządzenie wskazujące jest przenoszone poza element A...
5.mouseoutA
6.mouseleaveA

Kiedy urządzenie wskazujące jest przenoszone nad element A, a następnie do zagnieżdżonego elementu B, i na koniec poza element A, to następująca sekwencja zdarzeń musi wystąpić:

Nazwa zdarzeniaElementUwagi
1.mousemove
Urządzenie wskazujące jest przenoszone nad element A...
2.mouseoverA
3.mouseenterA
4.mousemoveAWielokrotne zdarzenia
Urządzenie wskazujące jest przenoszone nad zagnieżdżony element B...
5.mouseoutA
6.mouseoverB
7.mouseenterB
8.mousemoveBWielokrotne zdarzenia
Urządzenie wskazujące jest przenoszone z elementu B do A...
9.mouseoutB
10.mouseleaveB
11.mouseoverA
12.mousemoveAWielokrotne zdarzenia
Urządzenie wskazujące jest przenoszone poza element A...
13.mouseoutA
14.mouseleaveA

W niektórych przypadkach elementy mogą być wizualnie nałożone przy użyciu CSS. W poniższym przykładzie trzy elementy oznaczone jako A, B i C mają na stronie internetowej identyczne wymiary i pozycję absolutną. W drzewie DOM element C jest dzieckiem elementu B, i element B jest dzieckiem elementu A:

Graficzna reprezentacja trzech elementów ułożonych jeden na drugim, kiedy urządzenie wskazujące jest przemieszczane nad stosem.

Rysunek. Graficzna reprezentacja trzech elementów ułożonych jeden na drugim, kiedy urządzenie wskazujące jest przemieszczane nad stosem.

Kiedy urządzenie wskazujące jest przenoszone spoza stosu nad element C, a następnie z powrotem poza stos, to następująca sekwencja zdarzeń musi wystąpić:

Nazwa zdarzeniaElementUwagi
1.mousemove
Urządzenie wskazujące jest przenoszone nad element C (najwyższy element stosu)
2.mouseoverC
3.mouseenterA
4.mouseenterB
5.mouseenterC
6.mousemoveCWielokrotne zdarzenia
Urządzenie wskazujące jest przenoszone poza element C...
7.mouseoutC
8.mouseleaveC
9.mouseleaveB
10.mouseleaveA

Zdarzenia mouseover oraz mouseout są odpalane tylko raz, kiedy zdarzenia mouseenter oraz mouseleave są odpalane trzy razy (po razie dla każdego elementu).

Oto typowa sekwencja zdarzeń, kiedy przycisk powiązany z urządzeniem wskazującym (np. przycisk myszy lub trackpad) zostanie wciśnięty i zwolniony nad elementem:

Nazwa zdarzeniaUwagi
1.mousedown
2.mousemoveOPCJONALNIE, wielokrotne zdarzenia, pewne ograniczenia
3.mouseup
4.click
5.mousemoveOPCJONALNIE, wielokrotne zdarzenia, pewne ograniczenia
6.mousedown
7.mousemoveOPCJONALNIE, wielokrotne zdarzenia, pewne ograniczenia
8.mouseup
9.click
10.dblclick

Czas opóźnienia, stopień, odległość oraz liczba zdarzeń mousemove dopuszczalnych między zdarzeniami mousedown i mouseup przy jednoczesnym odpalaniu zdarzenia click lub dblclick są zależne od implementacji, urządzenia oraz specyfiki platformy. Tolerancja ta może pomagać użytkownikom, którzy mają zaburzenia fizyczne, jak niestabilne dłonie w trakcie interakcji użytkowników z urządzeniem wskazującym.

Każda implementacja określa stosowną histerezę tolerancji, ale ogólnie powinna odpalić zdarzenia click i dblclick, gdy cel zdarzenia skojarzony ze zdarzeniami mousedown i mouseup jest tym samym elementem bez interwencji zdarzeń mouseout lub mouseleave, i powinna odpalić zdarzenia click i dblclick na najbliższym wspólnym przodku obejmującym, kiedy cele zdarzeń skojarzone ze zdarzeniami mousedown i mouseup są różne.

Dla przykładu, jeśli zdarzenie mousedown zostało skierowane do HTML-owego elementowego ciała dokumentu i odpowiadające zdarzenie mouseup zostało skierowane do elementowego korzenia, to zdarzenie click zostanie wysłane do elementowego korzenia, ponieważ jest to najbliższy wspólny przodek obejmujący.

Prosty przykład:

  1. L
  2. K
  3. T'
  4. T
  5. A
  6. O
  7. Z'
  8. Z
  9. #
<!DOCTYPE html>
<html>

<head>

	<style>

		body > * {
			-webkit-touch-callout: none;
			-webkit-user-select: none;
			-khtml-user-select: none;
			-moz-user-select: none;
			-ms-user-select: none;
			user-select: none;
		}

	</style>

<body>

	<div style="background-color: #CCC; border: 1px solid #888;">
		Jakiś DIV1. Przeciągnij mnie do poniższego akapitu by otrzymać zdarzenie click.
	</div>
	<p>Jakiś akapit P1 na zewnątrz DIV1.</p>

	<div style="background-color: #FFF; border: 1px solid #888;">Kolejny DIV2. Przeciągnij mnie do poniższego akapitu by otrzymać zdarzenie click.
		<p>Kolejny akapit P2 wewnątrz DIV2.</p>
	</div>

	<p style="color: blue;">Szczegółowe informacje dla przechwyconego zdarzenia:</p>
	<div id="info" style="border: 1px solid #888;"></div>

	<script>

	var info = document.getElementById("info");

		window.addEventListener("mousedown", function(e){
			info.innerHTML += "e.type: " + e.type
			+ "<br>" + "e.target: " + e.target + "<br><br>";
		});

		window.addEventListener("mouseup", function(e){
			info.innerHTML += "e.type: " + e.type
			+ "<br>" + "e.target: " + e.target + "<br><br>";
		});

		window.addEventListener("click", function(e){
			info.innerHTML += "e.type: " + e.type
			+ "<br>" + "e.target: " + e.target + "<br><br>";
		});

	</script>

</body>

</html>

Jeśli cel zdarzenia (np. element docelowy) jest usuwany z drzewa DOM w czasie sekwencji zdarzeń myszy, to pozostałe zdarzenia sekwencji nie mogą być odpalane na tym elemencie.

Dla przykładu, jeśli element docelowy jest usuwany z drzewa DOM jako wynik zdarzenia mousedown, to żadne zdarzenia dla tego elementu nie zostaną wysłane dla zwolnienia przycisku (mouseup), kliknięcia (click) lub podwójnego kliknięcia (dblclick), ani żadne domyślne zdarzenia aktywacji. Jednakże zdarzenie mouseup wciąż jest wysyłane do elementu, który jest podatny na akcje myszy po usunięciu inicjującego elementu docelowego. Podobnie, jeśli element docelowy jest usuwany z drzewa DOM w trakcie wysyłania zdarzenia mouseup, to zdarzenia click i późniejsze nie będą wysyłane.

Uwagi praktyczne#

Wszystkie zdarzenia myszy dotyczące przesuwania mogą stanowić nie lada wyzwanie dla osób początkujących. Przykładowe kolejności stają się zrozumiałe dopiero po jakimś czasie, dlatego też zaproponuję nieco lżejszy ich wariant z dokładnym omówieniem podstaw.

Zakładamy na wstępie, że pojęcie przemieszczania myszy może dotyczyć dwóch kluczowych granic elementów:

granica wizualna

Kursor myszy przemieszcza się między granicami elementów, które jesteśmy w stanie dostrzec. Elementy mogą być ułożone obok siebie lub być zagnieżdżone, ale zawsze mamy wyraźną możliwość przejścia z jednej granicy do innej. Wariant ten będzie dotyczył zdarzeń typu mouseover i mouseout. Ich największą wadą jest bąbelkowanie, dlatego obsługa elementów jest utrudniona (wymaga dokładnej filtracji). Każde przekroczenie granicy wizualnej może spowodować odpalenie wymienionych zdarzeń, nawet jeśli przechodzimy z elementu potomka do elementu przodka (i na odwrót).

Załóżmy, że mamy trzy pozagnieżdżane bloki <div>, gdzie ich rzeczywista wizualizacj ma postać:

Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy

Rysunek. Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy

Oto krótkie omówienie sekwencji przemieszczania urządzenia wskazującego:

  • outside > DIV1 - zdarzenie mouseover zostanie odpalone dla bloku DIV1.
  • DIV1 > DIV2 - zdarzenie mouseover zostanie odpalone dla bloku DIV2.
  • DIV2 > DIV3 - zdarzenie mouseover zostanie odpalone dla bloku DIV3.
  • DIV3 > DIV2 - zdarzenie mouseover zostanie odpalone dla bloku DIV2.
  • DIV2 > DIV1 - zdarzenie mouseover zostanie odpalone dla bloku DIV1.

Ciekawostką jest to, że dla bardzo szybkich ruchów myszą (i oczywiście odpowiednich ustawień środowiska), można zapobiec odpaleniu wymienionych zdarzeń dla niektórych elementów, nawet jeśli granice między nimi były wyraźne. Dla przykładu, przesuwając szybko mysz w sekwencji DIV1 > DIV3 możemy spowodować, że zdarzenie mouseover zostanie odpalone jedynie dla bloku DIV3 (pomijając przy tym DIV2).

W przypadku elementów zagnieżdżonych o tych samych wymiarach i ułożeniu, które po prostu na siebie idealnie nachodzą, uwzględniany będzie jedynie ten na wierzchu:

Teoretyczna reprezentacja nałożonych granic wizualnych dla zdarzeń myszy

Rysunek. Teoretyczna reprezentacja nałożonych granic wizualnych dla zdarzeń myszy

Przemieszczenie kursora w obręb bloku DIV3 spowoduje, że tym razem odpalone zostanie tylko jedno zdarzenie mouseover właśnie dla bloku DIV3, który pod względem wizualnym idealnie przykrywa swoich przodków.

Zdarzenie typu mousemove również możemy zaliczyć do tego grona. Będzie ono odpalane jedynie dla widocznego elementu, który znajduje się na samym wierzchu.

granica strukturalna

W tym wypadku granica elementów tworzona jest przez strukturę znacznikową. Elementy mogą być ułożone obok siebie lub być zagnieżdżone, ale nie musimy mieć wyraźnej możliwość przejścia z jednej granicy do innej. Wariant ten będzie dotyczył zdarzeń typu mouseenter i mouseleave. Ich największą zaletą jest brak bąbelkowania i mniejsza liczba wystąpień. Nie każde przekroczenie granicy wizualnej będzie powodowało odpalenie wymienionych zdarzeń, np. przejście z elementu potomnego do przodka nie zostanie uwzględnione (przodek nie otrzyma zdarzenia mouseenter). Dzieje się tak dlatego, gdyż urządzenie wskazujące wciąż pozostaje w obrębie granicy strukturalnej przodka, którą to osiągnięto jeszcze przed wykonaniem ruchów.

Załóżmy, że mamy trzy pozagnieżdżane bloki <div>, gdzie ich rzeczywista wizualizacj ma postać:

Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy

Rysunek. Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy

Oto krótkie omówienie sekwencji przemieszczania urządzenia wskazującego:

  • outside > DIV1 - zdarzenie mouseenter zostanie odpalone dla bloku DIV1.
  • DIV1 > DIV2 - zdarzenie mouseenter zostanie odpalone dla bloku DIV2.
  • DIV2 > DIV3 - zdarzenie mouseenter zostanie odpalone dla bloku DIV3.
  • DIV3 > DIV2 - zdarzenie mouseenter nie zostanie odpalone dla bloku DIV2 (z perspektywy struktury kursor wciąż pozostaje w obrębie znaczników DIV2).
  • DIV2 > DIV1 - podobnie jak wyżej, zdarzenie mouseenter nie zostanie odpalone dla bloku DIV1.

W przypadku elementów zagnieżdżonych o tych samych wymiarach i ułożeniu, które po prostu na siebie idealnie nachodzą, uwzględniane będą wszystkie elementy. Ta sama uwaga dotyczy natychmiastowego przejścia z zewnętrznego elementu do najbardziej zagnieżdżonego (np. z obszaru poza blokiem DIV1 do bloku DIV3). Nawet szybkie ruchy myszą nie spowodują, że dla któregoś z elementów nie zostanie odpalone zdarzenie mouseenter.

Na podobnej zasadzie należy zinterpretować odpowiadające typy zdarzeń sygnalizujące opuszczanie granic elementów (mouseout i mouseleave). Zależności między nimi wszystkimi wyrażają przykładowe kolejności.

Pasek społecznościowy

SPIS TREŚCI AKTUALNEJ STRONY

Zdarzenia (H1) Mysz (H2) Kolejność zdarzeń myszy (H3) Uwagi praktyczne (H3)