mouseenter без jQuery

#javascript #javascript-events

#javascript #dom-события

Вопрос:

Каким был бы наилучший способ реализовать mouseenter / mouseleave подобное событие в JavaScript без jQuery? Какова наилучшая стратегия для кроссбраузерного использования? Я думаю о какой-то проверке свойства event.relatedTarget / event.toElement в mouseover / mouseout обработчиках событий?

Хотелось бы услышать ваши мысли.

Ответ №1:

(Полностью изменил мой ужасный ответ. Давайте попробуем еще раз.)

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

 var addEvent = window.addEventListener ? function (elem, type, method) {
        elem.addEventListener(type, method, false);
    } : function (elem, type, method) {
        elem.attachEvent('on'   type, method);
    };

var removeEvent = window.removeEventListener ? function (elem, type, method) {
        elem.removeEventListener(type, method, false);
    } : function (elem, type, method) {
        elem.detachEvent('on'   type, method);
    };
  

(Я знаю, довольно просто.)

Всякий раз, когда вы реализуете mouseenter / mouseleave, вы просто присоединяете события к обычным событиям наведения / вывода мыши, но затем проверяете две важные детали:

  1. Целью события является правильный элемент (или дочерний элемент правильного элемента)
  2. Связанная цель события не является дочерней по отношению к цели

Итак, нам также нужна функция, которая проверяет, является ли один элемент дочерним по отношению к другому:

 function contains(container, maybe) {
    return container.contains ? container.contains(maybe) :
        !!(container.compareDocumentPosition(maybe) amp; 16);
}
  

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

Итак, в итоге мы получаем что-то вроде этого:

 function mouseEnterLeave(elem, type, method) {
    var mouseEnter = type === 'mouseenter',
        ie = mouseEnter ? 'fromElement' : 'toElement',
        method2 = function (e) {
            e = e || window.event;
            var target = e.target || e.srcElement,
                related = e.relatedTarget || e[ie];
            if ((elem === target || contains(elem, target)) amp;amp;
                !contains(elem, related)) {
                    method();
            }
        };
    type = mouseEnter ? 'mouseover' : 'mouseout';
    addEvent(elem, type, method2);
    return method2;
}
  

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

 var div = document.getElementById('someID'),
    listener = function () {
        alert('do whatever');
    };

mouseEnterLeave(div, 'mouseenter', listener);
  

Чтобы удалить событие, вам нужно было бы сделать что-то вроде этого:

 var newListener = mouseEnterLeave(div, 'mouseenter', listener);

// removing...
removeEvent(div, 'mouseover', newListener);
  

Это вряд ли идеально, но все, что осталось, это просто детали реализации. The
важной частью было предложение if: mouseenter / mouseleave — это просто
наведение курсора мыши, но проверка, нацелен ли вы на правильный элемент, и если
связанный целевой объект является дочерним объектом целевого объекта.

Комментарии:

1. Этот скрипт, к сожалению, запускался около 40-50 раз на одном mouseenter. Если вы выполните простое mouseEnterLeave(ele, 'mouseenter', function(){ console.log('TEST'); }); , вы получите «test» в вашей консоли примерно 40 раз.

2. @OscarGodson Не могли бы вы выразиться более конкретно? Я попробовал очень простой пример в нескольких браузерах и не смог воспроизвести вашу проблему. (Хотя я нашел глупую ошибку IE, которую я включил в ответ.)

3. «функция, которая проверяет, является ли один элемент дочерним по отношению к другому». Ты имеешь в виду потомка , верно?

Ответ №2:

Лучший способ, имхо, создать свою собственную систему событий.

Несколько лет назад Дин Эдвардс написал один, из которого я взял подсказки в прошлом. Однако его решение работает «из коробки».

http://dean.edwards.name/weblog/2005/10/add-event/

Ответ №3:

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

Также не помешает время от времени просматривать jQuery, исходный код DOJO, чтобы на самом деле увидеть лучшие практики, которые они используют, чтобы заставить его работать в кроссбраузерном режиме.

Комментарии:

1. На самом деле, решение, которое я опубликовал выше, было ответом на отправку запроса об отставке. Он решил, что ему не нравятся определенные аспекты того, как это работает, поэтому он перекодировал это. Отсюда и то, почему сообщение Эдвардса появилось месяцем позже. : ) Мне немного больше нравится решение Эдвардса (по многим причинам, которые он указывает)…. хотя я определенно фанат Resig и его работы.

2. спасибо, что упомянули об этом. концепция guid — это то, что перенял даже jQuery.

Ответ №4:

другой вариант заключается в том, чтобы отличать истинные mouseout события от поддельных (дочерних) событий с помощью тестирования совпадений. Вот так:

 elt['onmouseout']=function(evt){
  if (!mouse_inside_bounding_box(evt,elt)) console.debug('synthetic mouseleave');
}
  

Я использовал что-то подобное в Chrome и, будьте осторожны, это, казалось, сделало свое дело. Как только у вас есть надежное событие mouseleave, mouseenter становится тривиальным.