Myszka na stronie

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...
  • mouseover - kursor znalazł się na elemencie
  • mouseout - kursor opuścił dany element

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.

Ogólna składnia przypisania tych zdarzeń do obiektu jest następująca:


function showMe() {
    console.log(this);
}

document.addEventListener("DOMContentLoaded", function() {
    element.addEventListener('mousedown', showMe);
    element.addEventListener('mouseup', showMe);
    element.addEventListener('click', showMe);
});

Poniżej możesz sprawdzć opisane wyżej przypadki.

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.


//włączamy mouseover
element.addEventListener('mouseover', function() {...});

//mousemove - poruszanie się po elemencie
element.addEventListener('mousemove', function() {...});

//mouseout - opuszczamy element
element.addEventListener('mouseout', function() {...});
Najedź kursorem i sprawdź w konsoli

mouseover vs mouseenter, mouseout vs mouseleave

Istnieją też bardzo podobne eventy do mouseover i mouseout - czyli mouseenter i mouseleave. Różnią się one tym od swoich braci, że nie są odpalane dla dzieci danego elementu. Wyobraź sobie, że robisz proste menu. Po najechaniu na to menu (mouseover) chcesz coś wykonać. Zdarzenie to będzie się odpalać nie tylko przy krawędziach danego elementu, ale także przy każdym najechaniu na każde dziecko danego elementu. W przypadku mouseenter i mouseleave takiego problemu nie ma:


<ul class="mouseenter-test">
    <li><a href="#">Testowy link przypadkowy</a></li>
    <li><a href="#">Testowy link przypadkowy</a></li>
    <li><a href="#">Testowy link przypadkowy</a></li>
    <li><a href="#">Testowy link przypadkowy</a></li>
    <li><a href="#">Testowy link przypadkowy</a></li>
</ul>

const ul = document.querySelector('.mouseenter-test');
ul.addEventListener('mouseover', function(e) {
    console.log('mouseover', e.target)
});

ul.addEventListener('mouseenter', function(e) {
    console.log('mouseenter', e.target)
});

ul.addEventListener('mouseleave', function(e) {
    console.log('mouseleave', e.target)
});

ul.addEventListener('mouseout', function(e) {
    console.log('mouseout', e.target)
});

Wykrywanie skąd przybył kursor

No dobrze, ale skąd mamy wiedzieć, z którego bloku wjechał kursor na blok nr 2? Czy z bloku nr 1 czy z 3?

Dla niektórych zdarzeń istnieje właściwość relatedTarget, która mówi nam z jakiego elementu wskaźnik przybył (dla zdarzenia mouseover) i na jaki element zdąża (dla zdarzenia mouseout).

Zdarzenie e.target e.relatedTarget
focusin Element który dostał focus Element który utracił focus
focusout Element który utracił focus Element który dostał focus
mouseenter Na który element najechał wskaźnik/kursor Który element opuścił wskaźnik/kursor
mouseleave Który element opuścił wskaźnik/kursor Na który element najechał wskaźnik/kursor
mouseout Który element opuścił wskaźnik/kursor Na który element najechał wskaźnik/kursor
mouseover Na który element najechał wskaźnik/kursor Który element opuścił wskaźnik/kursor
dragenter Na który element najechał wskaźnik/kursor Który element opuścił wskaźnik/kursor
dragexit Który element opuścił wskaźnik/kursor Na który element najechał wskaźnik/kursor

Sprawdź poniższy kod w konsoli najeżdżając na bloki kursorem

Najedź kursorem i przetestuj w konsoli

Pozycja kursora

Aby pobrać pozycję kursora na stronie możemy skorzystać z 2 par właściwości:

e.pageX Zwraca pozycję kursora od lewej krawędzi strony (wraz z przewinięciem)
e.pageY Zwraca pozycję kursora od górnej krawędzi strony (wraz z przewinięciem)
e.clientX
Zwraca pozycję kursora od górnej krawędzi widocznego obszaru strony (bez uwzględnienia pozycji przewinięcia strony)
e.clientY Zwraca pozycję kursora od lewej krawędzi widocznego obszaru strony (bez uwzględnienia pozycji przewinięcia strony)
e.screenX Zwraca pozycję kursora od lewej krawędzi ekranu
e.screenY Zwraca pozycję kursora od górnej krawędzi ekranu

Właściwości e.pageX i e.pageY zwracają realną pozycję kursora od początku strony, dlatego idealnie się nadają do "przyklejania" elementów do kursora - np. jakiś dymków, menusów itp.

Właściwości e.clientX i e.clientY zwracają pozycję kursora od krawędzi widocznego obszaru strony (tego który aktualnie widać w oknie przeglądarki), dlatego używając ich powinniśmy do nich doliczać właściwości document.body.scrollLeft i document.body.scrollTop, ponieważ strona może być przewinięta.

Pozycja myszki

element.addEventListener('mousemove', function(e) {
    console.log('Pozycja myszki:');
    console.log('x: ', e.pageX);
    console.log('y: ', e.pageY);
});

//inny przykład

element.addEventListener('click', function() {
    console.log('Pozycja myszki:');
    console.log('x: ', e.clientX + document.body.scrollLeft);
    console.log('y: ', e.clientY + document.body.scrollTop);
});
Najedź by pokazać pozycję kursora

A co gdybyś chciał sobie pobrać pozycję kursora nie na stronie, a na danym elemencie - powiedzmy na poniższym divie?

Zanim to zrobimy, wypiszmy w konsoli ten element (wklej poniższy kod do konsoli debugera):


const div = document.querySelector('.test-pos');
console.log( div );
console.dir( div );

Gdy rozwiniesz wypisany w ten sposób w konsoli element, pojawi ci się masa właściwości, z których możesz skorzystać.
Wśród nich znajdziesz takie jak offsetWidth, offsetHeight ale także offsetLeft i offsetTop. Te dwie ostatnie zawierają pozycję elementu od krawędzi strony. Pozostaje je wykorzystać odejmując je od pozycji kursora:


const div = document.querySelector('.test');

div.addEventListener('mousemove', function(e) {
    const x = e.pageX - this.offsetLeft;
    const y = e.pageX - this.offsetTop;

    console.log(x, y);
});

div.addEventListener('click', function() {
    console.clear();
});
Najedź kursorem i sprawdź w konsoli. Kliknij na div by wyczyścić konsolę

Alternatywą dla offsetLeft i offsetTop jest użycie funkcji getBoundingClientRect, która zwraca pozycję i wymiary danego elementu względem górnego lewego rogu aktualnego okna.

W wielu momentach metoda ta będzie wręcz wymagana, ponieważ offsetLeft i offsetTop nie uwzględniają w swoich wynikach transformacji (danego elementu ale i któregoś z rodziców).

Zostałem przesunięty w lewo o 150px za pomocą transformacji.

Kliknij mnie i sprawdź w konsoli

const div = document.querySelector('.test-pos-1');
const rect = div.getBoundingClientRect();

console.log(rect.left); //pozycja od lewej krawędzi okna
console.log(rect.top); //pozycja od górnej krawędzi okna
console.log(rect.right); //pozycja od lewej krawędzi okna
console.log(rect.bottom); //pozycja od górnej krawędzi okna

console.log(rect.right - rect.left); //szerokość elementu
console.log(rect.bottom - rect.top); //wysokość elementu
console.log(rect.left + window.scrollX); //pozycja left na stronie
console.log(rect.top + window.scrollY); //pozycja top na stronie

I zastosowanie w powyższym przykładzie:

Najedź kursorem i sprawdź w konsoli. Kliknij na div by wyczyścić konsolę

{
    const div = document.querySelector('.test-pos-2');

    div.addEventListener('mousemove', function(e) {
        const rect = this.getBoundingClientRect();
        const x = e.pageX - (rect.left + window.scrollX);
        const y = e.pageY - (rect.top + window.scrollY);
        console.log(x, y);
    });

    div.addEventListener('click', function() {
        console.clear();
    })
}

Który przycisk myszki

Aby odczytać, który przycisk został naciśnięty skorzystamy z właściwości button.

Guziki myszki zostały obdarowane odpowiednimi wartościami:

  • lewy guzik - 0
  • środkowy guzik - 1
  • prawy guzik - 2
Kliknij RMB by pokazać który klawisz został naciśnięty

function buttonMouse(e) {
    e.preventDefault();

    alert('Numer kliknietego przycisku: '+ e.button);
}

const block = document.querySelector('#blockBtn');
block.addEventListener('mousedown', buttonMouse);

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;
    padding:0;
    margin:0;
    width:300px;
    padding:10px;
    border-radius: 4px;
    box-shadow:0 2px 4px rgba(0,0,0,0.3);
    font-size:13px;
}
.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', function(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 scrollu (bo tak - przynajmniej u mnie zachowuje się to klasyczne menu):


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', function(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