Определить, над каким элементом находится указатель мыши в JavaScript

111

Мне нужна функция, которая сообщала бы мне, над каким элементом находится курсор мыши.

Так, например, если указатель мыши находится над этим текстовым полем (с идентификатором wmd-input), вызов window.which_element_is_the_mouse_on()будет функционально эквивалентен $("#wmd-input").

Том Леман
источник

Ответы:

148

ДЕМО

Есть действительно классная функция, document.elementFromPointкоторая делает то, на что похоже.

Что нам нужно, так это найти координаты x и y мыши, а затем вызвать ее, используя эти значения:

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

document.elementFromPoint

объект события jQuery

qwertymk
источник
1
@TikhonJelvis: Отсюда я вижу, что он поддерживается в IE и firefox. Если вы получаете эти два, вы обычно получаете их все. MSDN MDN
qwertymk
1
Потрясающие. Есть ли способ получить координаты мыши БЕЗ ловли события? (Вероятно, я не предполагаю). Если это невозможно, то, к сожалению, я не думаю, что этот метод лучше, чем event.targetили что-то еще
Том Леман,
1
@HoraceLoeb Невозможно получить координаты мыши, не поймав событие. см. этот вопрос SO для дальнейшего объяснения
Логан Бесекер
2
Это странный способ сделать это. Если вы поймали событие, почему бы просто не использовать event.target?
pmrotule
3
Ответ не объясняет, что это eventтакое и как оно появилось
vsync
64

В более новых браузерах вы могли делать следующее:

document.querySelectorAll( ":hover" );

Это даст вам список NodeList элементов, над которыми в данный момент находится указатель мыши, в порядке документа. Последний элемент в NodeList является наиболее конкретным, каждый предшествующий элемент должен быть родителем, дедушкой и дедушкой и так далее.

дхерман
источник
2
Похоже, это не сработало, пока я некоторое <li>время перетаскивал поверх других <li>элементов.
Seiyria
2
Я создал скрипку, $(':hover')но в основном это то же самое: jsfiddle.net/pmrotule/2pks4tf6
pmrotule
3
(function(){ var q = document.querySelectorAll(":hover"); return q[q.length-1]; })()
пользователь
3
У меня такое чувство, что производительность у него ужасная ... вызов этого mousemoveможет повредить производительность
vsync
49

Хотя нижеследующее может фактически не отвечать на вопрос, поскольку это первый результат поиска в Google (гуглер может не задавать точно такой же вопрос :), надеюсь, он предоставит дополнительные данные.

На самом деле существует два разных подхода к получению списка всех элементов, над которыми в данный момент находится мышь (возможно, для новых браузеров):

«Структурный» подход - восходящее дерево DOM

Как и в ответе Дхермана , можно позвонить

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

Однако это предполагает, что только дочерние элементы будут перекрывать своих предков, что обычно имеет место, но в целом неверно, особенно при работе с SVG, где элементы в разных ветвях дерева DOM могут перекрывать друг друга.

«Визуальный» подход - основан на «визуальном» перекрытии.

Этот метод используется, document.elementFromPoint(x, y)чтобы найти самый верхний элемент, временно скрыть его (поскольку мы восстанавливаем его немедленно в том же контексте, браузер фактически не будет его отображать), а затем перейдите к поиску второго самого верхнего элемента ... Выглядит немного хакерским, но он возвращает то, что вы ожидаете, когда есть, например, элементы-братья и сестры в дереве, перекрывающие друг друга. Пожалуйста, найдите этот пост для более подробной информации,

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;
}

Попробуйте оба варианта и проверьте их разные варианты возврата.

Herrlich10
источник
Это мне очень помогло. Спасибо @ herrlich10. Удивительно, но ваш код также обрабатывает случай с вложенными дочерними элементами.
Викрам Дешмук,
Это очень полезно. Именно то, что я искал. Как вы сказали, querySelectorAll работает не во всех сценариях.
noobsharp
4
@ herrlich10 Похоже, это неплохой полифил для developer.mozilla.org/en-US/docs/Web/API/Document/… . Если этот API существует в вашем поддерживаемом браузере, я думаю, вы могли бы его использовать.
dherman 08
Спасибо за это - работает почти так же, как и ранее (хотя нет elements.reverse для совместимости с getElementsFromPoint), но это намного медленнее (18-20 мс на вызов в моем тестировании в Chrome), что затрудняет использование в сценарии, который у меня был в разум (поиск целей для перетаскивания во время перетаскивания в случае, когда использование более простого подхода, управляемого событиями, невозможно).
jsdw
1
см. Document.elementsFromPoint(x, y) stackoverflow.com/a/31805883/1059828
Карл Адлер,
21

elementFromPoint()получает только первый элемент в дереве DOM. Чаще всего этого недостаточно для нужд разработчиков. Чтобы получить более одного элемента, например, в текущей позиции курсора мыши, вам нужна следующая функция:

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

Это возвращает массив всех объектов элементов в данной точке. Просто передайте этой функции значения X и Y мыши.

Дополнительная информация здесь: DocumentOrShadowRoot.elementsFromPoint ()

Для очень старых браузеров, которые не поддерживаются, вы можете использовать этот ответ как запасной вариант.

Карл Адлер
источник
3
Сейчас это хорошо поддерживается в 2018 году.
Мальчик,
6

События при наведении указателя мыши всплывают, поэтому вы можете поместить одного слушателя в тело и дождаться, пока они всплывут, а затем нажмите event.targetили event.srcElement:

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

<body onmouseover="doSomething(getTarget(event));">
РобГ
источник
Хотя я сам использую универсальные обработчики событий, это будет более затратным ресурсом, чем document.elementFromPoint(x, y).
Джон
6

Следующий код поможет вам получить элемент указателя мыши. Полученные элементы отобразятся в консоли.

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})
Сайкумар
источник
1
Это требует перемещения курсора. Насколько это возможно, здесь спрашивают не об этом.
Carles Alcolea
Не работает, если страница прокручивается. Используйте e.clientXи e.clientYвместо (проверено в Firefox 59).
youen
5

Вы можете посмотреть цель mouseoverсобытия на подходящем предке:

var currentElement = null;

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

Вот демо.

Ry-
источник
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. -->
Дип М
источник
3
Приношу извинения за плохое отношение к вашему (теперь удаленному) вопросу. Лично я бы предпочел, чтобы вопрос был восстановлен. Пока он был на пути к закрытию (на мой взгляд, несправедливо), я планировал проголосовать за его повторное открытие. Однако, учитывая количество голосов против, я понимаю, если вы решите оставить его удаленным.
Halfer
3

Демо: D

Наведите указатель мыши на окно фрагмента: D

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

Зибри
источник
1

Целью mousemoveсобытия DOM является самый верхний элемент DOM под курсором при перемещении мыши:

(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;
        }
    });
})();

Это похоже на решение @Philip Walton, но не требует jQuery или setInterval.

Макс Хейбер
источник
1

Вы можете использовать этот селектор, чтобы скрыть объект, а затем управлять им как объектом jQuery:

$(':hover').last();
Серж Гордеев
источник
4
Добро пожаловать в Stack Overflow! Пожалуйста, объясните, почему этот код помогает OP. Это поможет дать ответ будущим зрителям. Видеть Как ответить» для получения дополнительной информации. Тоже «подмышь»?
Heretic Monkey
Что такое «подмыть объект» ? Вы имеете в виду «объект под мышкой» ? Или что-то другое?
Питер Мортенсен
0

Вот решение для тех, кто все еще борется. Вы хотите добавить mouseoverсобытие в «родительский» элемент дочернего элемента (ов), который вы хотите обнаружить. В приведенном ниже коде показано, как это сделать.

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)
});

Демо на CodePen

УЗОР
источник
0

Позвольте мне начать с того, что я не рекомендую использовать метод, который собираюсь предложить. Это гораздо лучше использовать событие с приводом развития и привязки событий только к элементам , которые вы заинтересованы в знании или не мышь находится над с mouseover, mouseout, mouseenter,mouseleave и т.д.

Если вам абсолютно необходимо знать, над каким элементом находится мышь, вам нужно написать функцию, связывающуюmouseover событие ко всему в DOM, а затем сохраняет то, что текущий элемент находится в некоторой переменной.

Можно было вот так:

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);
    }
}());

По сути, я создал немедленную функцию, которая устанавливает событие для всех элементов и сохраняет текущий элемент внутри замыкания, чтобы минимизировать ваш след.

Вот рабочая демонстрация, которая вызывает window.which_element_is_the_mouse_onкаждую секунду и записывает, какой элемент в данный момент находится на консоли.

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

Филип Уолтон
источник