Выполнение действия при наведении указателя мыши на элемент (или его дочерние элементы) (и выходе из него)

Представьте, что у меня есть следующие элементы:

+------------------+
|                  |
|    +----------------+
|    |                |
|    |                |
|    +----------------+
|                  |
+------------------+

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

parent.addEventListener('mouseover', function(e) {
  // log mouse is over me
}, false);

Если я наведу указатель мыши непосредственно на родителя снизу, я получу прямое событие, но из-за маршрутизации событий, если я наведу указатель мыши сбоку (сначала ударив ребенка), я также получу событие от дочернего элемента, когда оно всплывает. .

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

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

var isOver = false;

parent.addEventListener('mouseover', function(e) {
  if (isOver) return;
  isOver = true;
  // log mouse is over me
}, false);

parent.addEventListener('mouseout', function(e) {
  if (e.relatedTarget == this || e.relatedTarget.isDescendantOf(this)) return;
  isOver = false;
  // log mouse is leaving me
}, false);

По сути, в коде есть переменная состояния, чтобы определить, «перекрыла» ли мышь родительский элемент (либо через своего дочернего элемента, либо нет), и если событие срабатывает снова, пока «перекрывает» эти события, эти события игнорируются ... мы также фиксируем событие выхода мыши и спросите его .. является ли relatedTarget (цель, которую вы собираетесь ввести) Я? (родитель) или возможный ребенок МЕНЯ? если это так, то ничего не делайте ... в противном случае установите для состояния "перекрытие" значение false.

Итак, этот код действительно работает, но представьте, что я не могу использовать свойство relatedTarget для определения следующей цели, как бы вы поступили с таким поведением?

Я надеюсь, что это имеет смысл, я чувствую, что в событиях недостаточно контекста, чтобы делать это без relatedTarget, но я чувствовал, что вполне может быть угол в отношении изменения поведения свойства eventPhase (чтобы определить, является ли событие прямым или пузырьковым).

Также я не ищу ответов, в которых говорится: «Это не будет работать в IE, yadda yadda» .. Я работаю только против браузеров спецификации событий w3c прямо сейчас.


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

+------------------+
|                  |
|                  +--+
|                     |
|                     |
|                  +--+
|                  |
+------------------+

person meandmycode    schedule 04.11.2009    source источник


Ответы (2)


Я думаю, что вам нужны _1 _ (срабатывает, когда пользователь перемещает указатель мыши внутри границ объекта) и _ 2_ событие (срабатывает, когда пользователь перемещает указатель мыши за пределы объекта):

В отличие от события onmouseover, событие onmouseenter не всплывает. Другими словами, событие onmouseenter не срабатывает, когда пользователь наводит указатель мыши на элементы, содержащиеся в объекте, тогда как onmouseover срабатывает.

То же для onmouseleave.

Вот пример: http://jsbin.com/uputi (добавьте /edit к URL-адресу, чтобы изменить исходный код ):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 
<head> 
<title>Sandbox</title> 
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen"> 
#outer { width:200px; height:200px; border:1px solid #333; }
#inner { float:right; width:125px; height:80px; margin-right:-80px; border:1px solid #333; }
</style> 

<script> 
window.onload = function() {
  function mouseenter() {
    document.getElementById('dbg').innerHTML = 'mouse entered #outer boundary';
  }

  function mouseleave() {
    document.getElementById('dbg').innerHTML = 'mouse left #outer boundary';
  }

  var outer = document.getElementById('outer');

  if ('onmouseenter' in document.body) {
    // browser supports mouseenter/mouseleave natively (i.e., IE)
    outer.onmouseenter = mouseenter;
    outer.onmouseleave = mouseleave;
  }
  else {
    // simulate mouseenter/mouseleave in other browsers
    function wrap(fn) {
      return function(event) {
        var parent = event.relatedTarget;

        // Check if we're still within the same parent (traverse up parentNodes)
        while (parent && parent != this) {
          parent = parent.parentNode
        }

        if(parent != this) {
          // call the *real* mouseover/mouseout handler
          fn.call(this, event)
        }
      }
    }

    outer.onmouseover = wrap(mouseenter);
    outer.onmouseout = wrap(mouseleave);
  }
}
</script> 
</head> 
<body> 
  <div id="outer"> 
    <div><code>#outer</code></div> 
    <div>&nbsp;</div> 
    <div>&nbsp;</div> 
    <div id="inner"><code>#inner</code></div> 
  </div> 
  <div id="dbg"></div> 
</body> 
</html>

Он использует relatedTarget, как и вы (что отлично работает в стандартных браузерах FYI), но без государственного флага. См. эту статью, чтобы получить более подробную информацию о показанной здесь эмуляции. Вы можете видеть, что только одно событие mouseenter запускается при вводе элемента #outer, , а также элемента #inner. Это «пограничный» эффект, к которому вы стремитесь (я думаю).

Если у вас есть возможность включить jQuery в свое приложение, j Query переносит эти фантастические события в другие браузеры. (обратите внимание, что jQuery использует описанную в ней методику с некоторыми незначительными изменениями).

В прототипе 1.6.1 также есть эти события смоделировано.

person Crescent Fresh    schedule 04.11.2009
comment
Ах, я не знал, что здесь уже была стандартизация, однако проблема в том, что в объяснении используется тот же метод, что и у меня (он фильтрует события, чтобы увидеть, является ли relatedTarget или является дочерним по отношению к исходному элементу с событием). Возможно, это самый простой способ выяснить, почему нет других решений (зачем писать что-то еще?), Проблема в том, что я не могу использовать свойство relatedTarget ... думаю, я застрял с неразрешимой проблемой! - person meandmycode; 04.11.2009
comment
@meandmycode: полагаться на relatedTarget - это нормально. См. Мой обновленный ответ (самодостаточный). - person Crescent Fresh; 04.11.2009
comment
К сожалению, проблема не в том, что я не думаю, что relatedTarget является стандартным, просто его нельзя использовать для этого. Я высоко оцениваю усилия по оказанию помощи и, учитывая, что у меня не было странного требования не использовать relatedTarget, ваш совет звучит ... поэтому я отмечу его как правильный. - person meandmycode; 04.11.2009
comment
Вы упомянули в вопросе, что ищете ответ, соответствующий спецификации W3C ...? Этот ответ определенно не соответствует этим требованиям. - person Josh Stodola; 04.11.2009
comment
Джош, да, в моем вопросе говорится, что я хочу, чтобы это противоречило спецификации w3c, но с условием не использовать relatedTarget ... для всех, кто ищет эту проблему и задает этот вопрос, у них не будет требования не использовать relatedTarget, поэтому этот ответ будет быть полезным. - person meandmycode; 04.11.2009
comment
@meandmycode: Из вашего вопроса я не понял, что вы пытались избежать relatedTarget. Как вы думаете, почему вам следует избегать этого? - person Crescent Fresh; 04.11.2009
comment
На самом деле моя реальная проблема связана с событиями перетаскивания, но, будучи новичком, я решил, что вопрос о событиях мыши получит больше отклика, тот же трюк должен быть возможен с событиями перетаскивания, как и с событиями мыши, только в спецификации w3c указано, что свойство relatedTarget события dragleave должно иметь значение null, однако на самом деле не объясняется, почему. - person meandmycode; 04.11.2009
comment
@meandmycode: Вау, ладно. Так что этот ответ бесполезен, не так ли? На самом деле это заняло у меня некоторое время. - person Crescent Fresh; 04.11.2009
comment
Количество времени, которое вы посвятите ответам на вопросы, полностью зависит от вас. - person meandmycode; 05.11.2009
comment
Оказывается, это может быть ошибка, firefox 3.6 ДЕЙСТВИТЕЛЬНО имеет установленный relatedTarget и имеет отчет об ошибке для резервного копирования изменения, в котором указано, что relatedTarget имеет значение null, если явно не установлено при запуске события перетаскивания. Опять же, ваше решение и время не были пустой тратой, просто я бы изменил строку, в которой вы вводите / выводите мышь, и меняю ее на перетаскивание, и это сработает. - person meandmycode; 05.11.2009

Я думаю, вам просто нужно stopPropagation

person Josh Stodola    schedule 04.11.2009
comment
Привет, Джош, я понимаю, что stopPropagation остановит всплытие, а это означает, что мой слушатель событий не получит событие наведения курсора мыши, когда я вхожу справа (где я вхожу в дочерний элемент перед родителем) ... по сути, мне нужно обработать все видимая поверхность моего элемента (и его дочерних элементов) как одно целое, и когда я вхожу или покидаю всю поверхность, происходит одно событие «перекрытие» и «выход». - person meandmycode; 04.11.2009