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 zdarzenia | Element | Uwagi | |
---|---|---|---|
1. | mousemove | ||
Urządzenie wskazujące jest przenoszone nad element A... | |||
2. | mouseover | A | |
3. | mouseenter | A | |
4. | mousemove | A | Wielokrotne zdarzenia |
Urządzenie wskazujące jest przenoszone poza element A... | |||
5. | mouseout | A | |
6. | mouseleave | A |
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 zdarzenia | Element | Uwagi | |
---|---|---|---|
1. | mousemove | ||
Urządzenie wskazujące jest przenoszone nad element A... | |||
2. | mouseover | A | |
3. | mouseenter | A | |
4. | mousemove | A | Wielokrotne zdarzenia |
Urządzenie wskazujące jest przenoszone nad zagnieżdżony element B... | |||
5. | mouseout | A | |
6. | mouseover | B | |
7. | mouseenter | B | |
8. | mousemove | B | Wielokrotne zdarzenia |
Urządzenie wskazujące jest przenoszone z elementu B do A... | |||
9. | mouseout | B | |
10. | mouseleave | B | |
11. | mouseover | A | |
12. | mousemove | A | Wielokrotne zdarzenia |
Urządzenie wskazujące jest przenoszone poza element A... | |||
13. | mouseout | A | |
14. | mouseleave | A |
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
:
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 zdarzenia | Element | Uwagi | |
---|---|---|---|
1. | mousemove | ||
Urządzenie wskazujące jest przenoszone nad element C (najwyższy element stosu) | |||
2. | mouseover | C | |
3. | mouseenter | A | |
4. | mouseenter | B | |
5. | mouseenter | C | |
6. | mousemove | C | Wielokrotne zdarzenia |
Urządzenie wskazujące jest przenoszone poza element C... | |||
7. | mouseout | C | |
8. | mouseleave | C | |
9. | mouseleave | B | |
10. | mouseleave | A |
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 zdarzenia | Uwagi | |
---|---|---|
1. | mousedown | |
2. | mousemove | OPCJONALNIE, wielokrotne zdarzenia, pewne ograniczenia |
3. | mouseup | |
4. | click | |
5. | mousemove | OPCJONALNIE, wielokrotne zdarzenia, pewne ograniczenia |
6. | mousedown | |
7. | mousemove | OPCJONALNIE, 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:
<!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
imouseout
. 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ć:Rysunek. Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy
Oto krótkie omówienie sekwencji przemieszczania urządzenia wskazującego:
outside > DIV1
- zdarzeniemouseover
zostanie odpalone dla blokuDIV1
.DIV1 > DIV2
- zdarzeniemouseover
zostanie odpalone dla blokuDIV2
.DIV2 > DIV3
- zdarzeniemouseover
zostanie odpalone dla blokuDIV3
.DIV3 > DIV2
- zdarzeniemouseover
zostanie odpalone dla blokuDIV2
.DIV2 > DIV1
- zdarzeniemouseover
zostanie odpalone dla blokuDIV1
.
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 zdarzeniemouseover
zostanie odpalone jedynie dla blokuDIV3
(pomijając przy tymDIV2
).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:
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 zdarzeniemouseover
właśnie dla blokuDIV3
, 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
imouseleave
. 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 zdarzeniamouseenter
). 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ć:Rysunek. Teoretyczna reprezentacja wyraźnych granic wizualnych dla zdarzeń myszy
Oto krótkie omówienie sekwencji przemieszczania urządzenia wskazującego:
outside > DIV1
- zdarzeniemouseenter
zostanie odpalone dla blokuDIV1
.DIV1 > DIV2
- zdarzeniemouseenter
zostanie odpalone dla blokuDIV2
.DIV2 > DIV3
- zdarzeniemouseenter
zostanie odpalone dla blokuDIV3
.DIV3 > DIV2
- zdarzeniemouseenter
nie zostanie odpalone dla blokuDIV2
(z perspektywy struktury kursor wciąż pozostaje w obrębie znacznikówDIV2
).DIV2 > DIV1
- podobnie jak wyżej, zdarzeniemouseenter
nie zostanie odpalone dla blokuDIV1
.
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 blokuDIV3
). Nawet szybkie ruchy myszą nie spowodują, że dla któregoś z elementów nie zostanie odpalone zdarzeniemouseenter
.
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.