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", () => {...});
Najedź kursorem i sprawdź w konsoli

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
});
Kliknij i sprawdź

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
Najedź by pokazać pozycję kursora

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.

Kliknij RMB by pokazać który klawisz został naciśnięty

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.

Kliknij prawym by pokazać menu kontekstowe

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

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem.
Aha - i ta strona korzysta z ciasteczek.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.