Określ, na którym elemencie znajduje się wskaźnik myszy w JavaScript

111

Chcę funkcji, która mówi mi, nad którym elementem znajduje się kursor myszy.

Na przykład, jeśli mysz użytkownika znajduje się nad tym obszarem tekstowym (z identyfikatorem wmd-input), wywołanie window.which_element_is_the_mouse_on()będzie funkcjonalnie równoważne z $("#wmd-input").

Tom Lehman
źródło

Odpowiedzi:

148

PRÓBNY

Jest naprawdę fajna funkcja o nazwie, document.elementFromPointktóra robi to, na co wygląda.

Potrzebujemy znaleźć współrzędne x i y myszy, a następnie wywołać je przy użyciu tych wartości:

var x = event.clientX, y = event.clientY,
    elementMouseIsOver = document.elementFromPoint(x, y);

document.elementFromPoint

obiekt zdarzenia jQuery

qwertymk
źródło
1
@TikhonJelvis: Cóż, stąd widzę, że jest obsługiwany w IE i Firefox. Jeśli zdobędziesz te dwa, zwykle dostaniesz je wszystkie. MSDN MDN
qwertymk
1
Niesamowite. Czy jest sposób, aby uzyskać koordynaty myszy BEZ łapania zdarzenia? (Prawdopodobnie nie zakładam). Jeśli to nie jest możliwe, to niestety nie sądzę, aby ta metoda była lepsza niż event.targetczy cokolwiek innego
Tom Lehman
1
@HoraceLoeb Nie można uzyskać współrzędnych myszy bez przechwytywania zdarzenia. zobacz to pytanie SO w celu uzyskania dalszych wyjaśnień
Logan Besecker,
2
To dziwny sposób. Jeśli złapiesz wydarzenie, dlaczego po prostu nie użyć event.target?
pmrotule
3
Odpowiedź nie wyjaśnia, co eventjest i jak do tego doszło
vsync
64

W nowszych przeglądarkach możesz wykonać następujące czynności:

document.querySelectorAll( ":hover" );

To da ci NodeList elementów, nad którymi aktualnie znajduje się mysz w kolejności dokumentów. Ostatni element w NodeList jest najbardziej szczegółowy, każdy poprzedzający powinien być rodzicem, dziadkiem i tak dalej.

dherman
źródło
2
Wydawało się, że to nie działa, gdy przeciągałem przez <li>chwilę na inne <li>elementy.
Seiyria
2
Stworzyłem skrzypce, $(':hover')ale to w zasadzie to samo: jsfiddle.net/pmrotule/2pks4tf6
pmrotule
3
(function(){ var q = document.querySelectorAll(":hover"); return q[q.length-1]; })()
użytkownik
3
Mam przeczucie, że wykonanie tego jest okropne ... wywołanie tego mousemovemoże zaszkodzić wydajności
vsync
49

Chociaż poniższe informacje mogą w rzeczywistości nie odpowiadać na pytanie, ponieważ jest to pierwszy wynik wyszukiwania w Google (pracownik Google może nie zadać dokładnie tego samego pytania :), mam nadzieję, że dostarczy dodatkowych informacji.

W rzeczywistości istnieją dwa różne podejścia do uzyskania listy wszystkich elementów, nad którymi aktualnie znajduje się mysz (być może w przypadku nowszych przeglądarek):

Podejście „strukturalne” - rosnąco drzewo DOM

Jak w odpowiedzi Dhermana , można zadzwonić

var elements = document.querySelectorAll(':hover');

Jednak zakłada się, że tylko dzieci będą nakładać na swoich przodków, co zwykle ma miejsce, ale nie jest to prawdą w ogóle, szczególnie w przypadku SVG, w którym elementy w różnych gałęziach drzewa DOM mogą się nakładać.

Podejście „wizualne” - oparte na nakładaniu się „wizualnych”

Ta metoda wykorzystuje document.elementFromPoint(x, y)do znalezienia najwyższego elementu, tymczasowego ukrycia go (ponieważ odzyskujemy go natychmiast w tym samym kontekście, przeglądarka w rzeczywistości tego nie wyrenderuje), a następnie przejdź do znajdowania drugiego najwyższego elementu ... Wygląda trochę dziwnie, ale zwraca to, czego się spodziewasz, gdy np. w drzewie występują elementy rodzeństwa, które się blokują. Proszę znaleźć ten post, aby uzyskać więcej informacji,

function allElementsFromPoint(x, y) {
    var element, elements = [];
    var old_visibility = [];
    while (true) {
        element = document.elementFromPoint(x, y);
        if (!element || element === document.documentElement) {
            break;
        }
        elements.push(element);
        old_visibility.push(element.style.visibility);
        element.style.visibility = 'hidden'; // Temporarily hide the element (without changing the layout)
    }
    for (var k = 0; k < elements.length; k++) {
        elements[k].style.visibility = old_visibility[k];
    }
    elements.reverse();
    return elements;
}

Wypróbuj oba i sprawdź ich różne zwroty.

herrlich10
źródło
To mi bardzo pomogło. Dzięki @ herrlich10. Co zaskakujące, Twój kod obsługuje również sprawę z zagnieżdżonymi elementami potomnymi.
Vikram Deshmukh
Jest to niezwykle przydatne. Dokładnie to, czego szukałem. Jak już powiedziałeś, querySelectorAll nie działa w każdym scenariuszu.
noobsharp
4
@ herrlich10 To wydaje się być przyzwoitym wypełnieniem dla developer.mozilla.org/en-US/docs/Web/API/Document/… . Jeśli ten interfejs API istnieje w obsługiwanym zestawie przeglądarek, myślę, że możesz go użyć.
dherman,
Dzięki za to - działa prawie tak, jak oczekiwano (choć nie ma żadnych elementów.reverse dla kompatybilności z getElementsFromPoint), ale jest znacznie wolniejszy (18-20 ms na połączenie w moich testach w Chrome), co utrudnia użycie w scenariuszu, który miałem w umysł (znajdowanie celów upuszczania podczas przeciągania w przypadku, gdy stosuje się bardziej podstawowe podejście oparte na zdarzeniach, jest niewykonalne).
jsdw
1
patrz Document.elementsFromPoint(x, y) stackoverflow.com/a/31805883/1059828
Karl Adler,
21

elementFromPoint()pobiera tylko pierwszy element w drzewie DOM. To przeważnie nie wystarcza dla potrzeb programistów. Aby uzyskać więcej niż jeden element na przykład w bieżącej pozycji wskaźnika myszy, jest to funkcja, której potrzebujesz:

document.elementsFromPoint(x, y) . // Mind the 's' in elements

Zwraca tablicę wszystkich obiektów elementów w danym punkcie. Po prostu podaj wartości X i Y myszy do tej funkcji.

Więcej informacji znajduje się tutaj: DocumentOrShadowRoot.elementsFromPoint ()

W przypadku bardzo starych przeglądarek, które nie są obsługiwane, możesz użyć tej odpowiedzi jako rozwiązania zastępczego.

Karl Adler
źródło
3
Jest dobrze obsługiwany teraz w 2018 r.
Boy
6

Zdarzenia po najechaniu kursorem myszy pojawiają się w dymkach, więc możesz umieścić jednego słuchacza na ciele i poczekać, aż się rozwiną, a następnie złapać event.targetlub event.srcElement:

function getTarget(event) {
    var el = event.target || event.srcElement;
    return el.nodeType == 1? el : el.parentNode;
}

<body onmouseover="doSomething(getTarget(event));">
RobG
źródło
Chociaż sam używam uniwersalnych programów obsługi zdarzeń, byłoby to droższe pod względem zasobów niż document.elementFromPoint(x, y).
Jan
6

Poniższy kod pomoże Ci uzyskać element wskaźnika myszy. Wynikowe elementy zostaną wyświetlone w konsoli.

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})
saikumar
źródło
1
Wymaga to przesuwania kursora. Tak blisko, jak to tylko możliwe, nie o to tu chodzi.
Carles Alcolea
Nie działa, jeśli strona jest przewijana. Użyj e.clientXi e.clientYzamiast tego (testowane w przeglądarce Firefox 59).
youen
5

Możesz spojrzeć na cel mouseoverwydarzenia na jakimś odpowiednim przodku:

var currentElement = null;

document.addEventListener('mouseover', function (e) {
    currentElement = e.target;
});

Oto demo.

Ry-
źródło
4
<!-- One simple solution to your problem could be like this: -->

<div>
<input type="text" id="fname" onmousemove="javascript: alert(this.id);" />
<!-- OR -->
<input type="text" id="fname" onclick="javascript: alert(this.id);" />
</div>
<!-- Both mousemove over the field & click on the field displays "fname"-->
<!-- Works fantastic in IE, FireFox, Chrome, Opera. -->
<!-- I didn't test it for Safari. -->
Dip M.
źródło
3
Przepraszam za kiepski sposób, w jaki zostałeś potraktowany na Twoje (teraz usunięte) pytanie. Osobiście wolałbym, aby to pytanie nie zostało usunięte. Kiedy zbliżało się zamknięcie (moim zdaniem niesprawiedliwie), planowałem głosować za jego ponownym otwarciem. Jednak biorąc pod uwagę liczbę głosów przeciw, rozumiem, że zdecydujesz się je usunąć.
halfer
3

Demo: D

Przesuń kursor myszy w oknie fragmentu kodu: D

<script>
document.addEventListener('mouseover', function (e) {
    console.log ("You are in ", e.target.tagName);
});
</script>

Zibri
źródło
1

Celem mousemovezdarzenia DOM jest znajdujący się najwyżej element DOM pod kursorem, gdy porusza się mysz:

(function(){
    //Don't fire multiple times in a row for the same element
    var prevTarget=null;
    document.addEventListener('mousemove', function(e) {
        //This will be the top-most DOM element under cursor
        var target=e.target;
        if(target!==prevTarget){
            console.log(target);
            prevTarget=target;
        }
    });
})();

Jest to podobne do rozwiązania @Philip Walton, ale nie wymaga jQuery ani setInterval.

Max Heiber
źródło
1

Możesz użyć tego selektora, aby podkopać obiekt, a następnie manipulować nim jako obiektem jQuery:

$(':hover').last();
Serge Gordeev
źródło
4
Witamy w Stack Overflow! Proszę dodać wyjaśnienie, dlaczego ten kod pomaga OP. Pomoże to znaleźć odpowiedź, z której przyszli widzowie będą mogli się czegoś nauczyć. Aby uzyskać więcej informacji, zobacz Jak odpowiedzieć . A także „undermouse”?
Heretic Monkey,
Co to jest „podkopać obiekt” ? Czy masz na myśli „obiekt znajdujący się pod myszą” ? Albo coś innego?
Peter Mortensen
0

Oto rozwiązanie dla tych, którzy nadal mogą mieć problemy. Chcesz dodać mouseoverzdarzenie do elementu „nadrzędnego” elementu podrzędnego, który chcesz wykryć. Poniższy kod pokazuje, jak się do tego zabrać.

const wrapper = document.getElementById('wrapper') //parent element
const position = document.getElementById("displaySelection")

wrapper.addEventListener('mousemove', function(e) {
  let elementPointed = document.elementFromPoint(e.clientX, e.clientY)

  console.log(elementPointed)
});

Demo na CodePen

UZOR
źródło
0

Zacznę od stwierdzenia, że ​​nie polecam stosowania metody, którą mam zamiar zasugerować. Jest o wiele lepiej zdarzeniami rozwoju i wiązania zdarzeń używać tylko do elementów jesteś zainteresowany wiedząc, czy mysz jest nad z mouseover, mouseout, mouseenter,mouseleave , itd.

Jeśli absolutnie musisz mieć możliwość wiedzieć, który element znajduje się nad myszą, musiałbyś napisać funkcję, która wiąże mouseoverzdarzenie ze wszystkim w DOM, a następnie przechowywać cokolwiek, co znajduje się w bieżącym elemencie w jakiejś zmiennej.

Mógłbyś coś takiego:

window.which_element_is_the_mouse_on = (function() {

    var currentElement;

    $("body *").on('mouseover', function(e) {
        if(e.target === e.currentTarget) {
            currentElement = this;
        }
    });

    return function() {
        console.log(currentElement);
    }
}());

Zasadniczo stworzyłem natychmiastową funkcję, która ustawia zdarzenie na wszystkich elementach i przechowuje bieżący element w zamknięciu, aby zminimalizować twój ślad.

Oto działające demo, które wywołuje window.which_element_is_the_mouse_on co sekundę i rejestruje na konsoli element, na którym aktualnie znajduje się mysz.

http://jsfiddle.net/LWFpJ/1/

Philip Walton
źródło