Events - myszka
Do zdarzeń, które wywołuje kursor myszki zaliczamy:
mousedown | przycisk myszki został naciśnięty |
---|---|
mouseup | przycisk myszki został puszczony |
click | przycisk myszki został naciśnięty i puszczony (czyli normalne kliknięcie) |
dblclick | podwójne kliknięcie |
mousemove | kursor porusza się po elemencie |
mouseover | kursor znalazł się na elemencie |
mouseout | kursor opuścił dany element |
mouseenter | kursor znalazł się na elemencie |
mouseleave | kursor opuścił dany element |
contextmenu | zdarzenie odpalane po kliknięciu prawego guzika myszy (jak otwieranie podręcznego menu) |
Zdarzenia mousedown, mouseup, click
Zdarzenia te są związane z kliknięciem przyciskiem myszki na dany obiekt.
Istnieje pewna różnica miedzy click i mousedown. Przypuśćmy, że użytkownik nacisnął guzik na buttonie, następnie przesunął kursor poza ten button i puścił przycisk myszki. Button zarejestrował tylko zdarzenie mousedown. Podobnie - jeżeli użytkownik naciśnie guzik myszki poza buttonem, przesunie kursor na button i wtedy puści przycisk myszki, to button zarejestruje tylko zdarzenie mouseup.
W Windows dla większości przycisków stosowane jest zdarzenie click. Sprawdź sam - kliknij na którymś z guzików w prawym górnym rogu dowolnego okna (np. na zamykającym [X]), a następnie nie puszczając przycisku zjedź kursorem z tego guzika. Okno się nie zamknie, bo nie zaistniało zdarzenia click, tylko zdarzenie mousedown.
element.addEventListener("mousedown", () => {...});
element.addEventListener("mouseup", () => {...});
element.addEventListener("click", () => {...});
Zdarzenia mouseover, mousemove, mouseout
Zdarzenia mouseover, mousemove i mouseout służą do sprawdzenia, czy użytkownik najechał kursorem na element, czy się po nim porusza lub czy z kursorem opuścił dany element.
element.addEventListener("mouseover", () => {...});
element.addEventListener("mousemove", () => {...});
element.addEventListener("mouseout", () => {...});
mouseenter, mouseleave
Istnieją też bardzo podobne zdarzenia czyli mouseenter i mouseleave.
Różnią się one tym od swoich braci, że nie są odpalane dla dzieci danego elementu.
<ul class="mouseenter-test">
<li><a href="#">Testowy link</a></li>
<li><a href="#">Testowy link</a></li>
<li><a href="#">Testowy link</a></li>
<li><a href="#">Testowy link</a></li>
<li><a href="#">Testowy link</a></li>
</ul>
const ul = document.querySelector(".mouseenter-test");
ul.addEventListener("mouseover", () => {
console.log("mouseover")
});
ul.addEventListener("mouseenter", () => {
console.log("mouseenter")
});
ul.addEventListener("mouseleave", () => {
console.log("mouseleave")
});
ul.addEventListener("mouseout", () => {
console.log("mouseout")
});
Dodatkowe informacje
Tak jak w poprzednim rozdziale, aby pobrać dodatkowe informacje o zaistniałym zdarzeniu, wystarczy, że wypiszemy w konsoli wartość przekazanego do naszej funkcji parametru:
const element = document.querySelector(".element");
element.addEventListener("click", e => {
console.log(e); //MouseEvent
});
Jak zobaczysz, masz dostęp do wielu przydatnych właściwości. Te najczęściej używane omówimy sobie poniżej.
Pozycja kursora
Aby pobrać pozycję kursora na stronie możemy skorzystać z kilku par właściwości:
e.pageX e.pageY |
Zwraca pozycje X, Y kursora od górnego lewego narożnika strony |
---|---|
e.clientX e.clientY |
Zwraca pozycję X, Y kursora od lewej krawędzi widocznego obszaru strony (bez uwzględnienia pozycji przewinięcia strony) |
e.screenX e.screenY |
Zwraca pozycję X, Y na ekranie monitora (jeżeli masz wiele ekranów to od lewego górnego rogu ekranu leżącego najdalej po lewej stronie) |
e.layerX e.layerY |
Zwraca pozycję X, Y na warstwie renderowania. Nie są to standardowe właściwości (1) |
e.offsetX e.offsetY |
Zwraca pozycję X, Y kursora na od lewego górnego narożnika danego elementu |
x y |
W nowych wersjach przeglądarek są to aliasy dla clientX i clientY |
W większości przypadków głównie będziesz korzystać z właściwości e.pageX
, e.pageY
lub ewentualnie e.offsetX
i e.offsetY
. Wynika to z prostego faktu - zazwyczaj będziesz chciał pozycjonować jakieś elementy względem kursora, a pozycjonowanie takie robimy względem całej strony, a nie danego widoku.
Który przycisk myszki
Aby odczytać, który przycisk został naciśnięty skorzystamy z właściwości e.button oraz e.buttons. W przypadku tej drugiej możemy dodatkowo zareagować gdy oba przyciski myszy są naciśnięte równocześnie.
function buttonMouse(e) {
e.preventDefault();
console.log("e.button: " + e.button);
console.log("e.buttons: " + e.buttons);
}
const block = document.querySelector("#blockBtn");
block.addEventListener("mousedown", buttonMouse);
Przykład: celownik
Mając powyższe informacje, bez problemu moglibyśmy zrobić celownik na jakimś elemencie.
.board {
height: 500px;
border: 1px solid #ddd;
overflow: hidden;
position: relative;
cursor: none;
box-shadow: inset 0 0 0 30px rgba(0,0,0,0.05);
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
}
.cross {
width: 100px;
height: 100px;
border: 10px solid tomato;
border-radius: 50%;
box-sizing: border-box;
display: flex;
justify-content:center;
align-items: center;
position: absolute;
left: 0;
top: 0;
transform: translate(-50%, -50%);
box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
}
.cross::before {
content: "";
width: 10px;
height: 10px;
background: tomato;
border-radius: 50%;
}
<div class="board">
<div class="cross"></div>
</div>
const board = document.querySelector(".board");
const cross = board.querySelector(".cross");
board.addEventListener("mousemove", e => {
cross.style.left = `${e.offsetX}px`;
cross.style.top = `${e.offsetY}px`;
});
board.addEventListener("mouseover", () => {
cross.style.display = "";
});
board.addEventListener("mouseout", () => {
cross.style.display = "none";
});
Użyte właściwości e.offsetX
i e.offsetY
wskazują na pozycję kursora względem najechanej warstwy. By nie była ona liczona względem celownika a planszy po której jeździmy musimy dodać mu właściwość CSS pointer-events: none
:
.cross {
...,
pointer-events: none;
}
Przykład: menu kontekstowe
Zbierzmy kilka powyższych informacji, i zróbmy własne kontekstowe menu.
Nasze menu to zwykła lista UL pozycjonowana absolutnie. Domyślnie wysuwamy ją totalnie za ekran ustawiając top i left na -9999:
<ul class="menu-context">
<li><a href="">Element menu 1</a></li>
<li><a href="">Element menu 2</a></li>
<li><a href="">Element menu 3</a></li>
<li><a href="">Element menu 4</a></li>
</ul>
.menu-context {
position: absolute;
left: -9999px;
top: -9999px;
background: #222;
color: #fff;
list-style: none;
width: 300px;
padding: 10px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
font-size: 13px;
z-index: 10000;
}
.menu-context li {
border-bottom: 1px solid #444;
cursor: pointer;
}
.menu-context li a {
color: #fff;
text-decoration: none;
padding: 5px 10px;
display: block;
transition: 0.5s all;
}
.menu-context li a:hover {
background: #F15C5C;
color: #fff;
}
.menu-context li:last-child {
border: 0;
}
Dodajemy pokazywanie naszego menu po kliknięciu prawym przyciskiem na div:
const menu = document.querySelector(".menu-context");
function rightButton(e) {
e.preventDefault();
if (e.button === 2) {
e.stopPropagation();
menu.style.left = e.pageX + 10 + "px";
menu.style.top = e.pageY + 10 + "px";
}
}
document.querySelector("#blockRightBtn").addEventListener("mousedown", rightButton);
Jeżeli teraz klikniemy prawym przyciskiem myszy na div,
faktycznie nasze menu się pojawi, ale też pojawi się domyślne menu.
Żeby domyślne menu się nie pokazywało, powinniśmy użyć pewnie
e.preventDefault().
I faktycznie - tyle tylko, że musimy zatrzymać event contextmenu:
document.querySelector("#blockRightBtn").addEventListener("contextmenu", e => {
e.preventDefault();
});
Ostatnią rzeczą jaką musimy zrobić to możliwość schowania menu poprzez kliknięcie lewym przyciskiem myszy w dowolne miejsce na stronie i podczas przewijania strony (przynajmniej u mnie gdy zacznę przewijać stronę klasyczne menu się chowa):
const menu = document.querySelector(".menu-context");
function removeMenu() {
menu.style.left = "-9999px";
menu.style.top = "-9999px";
}
function rightButton(e) {
...
}
const block = document.querySelector("#blockRightBtn");
block.addEventListener("mousedown", rightButton);
block.addEventListener("contextmenu", e => {
e.preventDefault();
});
document.addEventListener("mousedown", removeMenu);
document.addEventListener("scroll", removeMenu);
Powyższe rozwiązanie jest prostym przykładem. W większych projektach prawdopodobnie o wiele lepiej sprawdzi się gotowy plugin np. https://github.com/electerious/basicContext
Trening czyni mistrza
Jeżeli chcesz sobie potrenować zdobytą wiedzę, zadania znajdują się w repozytorium pod adresem: https://github.com/kartofelek007/zadania