вернуться к уроку

"Умная" подсказка

важность: 5

Напишите функцию, которая показывает подсказку над элементом только в случае, когда пользователь передвигает мышь на него, но не через него.

Другими словами, если пользователь подвинул курсор на элементе и остановился – показывать подсказку. А если он просто быстро провёл курсором по элементу, то не надо ничего показывать. Кому понравится лишнее мелькание?

Технически, мы можем измерять скорость прохода курсора мыши над элементом, и если она низкая, то можно посчитать, что пользователь остановил курсор над элементом, и показать ему подсказку. А если скорость высокая, то тогда не показывать.

Создайте для этого универсальный объект new HoverIntent(options).

Его настройки options:

  • elem – отслеживаемый элемент.
  • over – функция, вызываемая, при заходе на элемент, считаем что заход – это когда курсор медленно двигается или остановился над элементом.
  • out – функция, вызываемая при уходе курсора с элемента (если был заход).

Пример использования такого объекта для показа подсказки:

// пример подсказки
let tooltip = document.createElement('div');
tooltip.className = "tooltip";
tooltip.innerHTML = "Tooltip";

// объект будет отслеживать движение мыши и вызывать функции over/out
new HoverIntent({
  elem,
  over() {
    tooltip.style.left = elem.getBoundingClientRect().left + 'px';
    tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';
    document.body.append(tooltip);
  },
  out() {
    tooltip.remove();
  }
});

Демо:

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

Обратите внимание: подсказка не должна пропадать (мигать), когда курсор переходит между дочерними элементами часов.

Открыть песочницу с тестами для задачи.

Алгоритм выглядит просто:

  1. Назначаем обработчики onmouseover/out на элементе. Также можно было бы использовать onmouseenter/leave, но они менее универсальны и не сработают с делегированием.
  2. Когда курсор переходит на элемент, начинаем измерять скорость его движения, используя mousemove.
  3. Если скорость низкая, то вызываем over.
  4. Когда мы выходим из элемента, если запускали over, вызываем out.

Но как измерить скорость?

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

К сожалению, в JavaScript нет возможности получать текущие координаты мыши. Не существует функции типа получитьТекущиеКоординатыМыши().

Единственный путь – это слушать события мыши, например mousemove, и координаты брать из объекта события.

Так что поставим обработчик на mousemove, чтобы отслеживать координаты и запоминать их. И будем сравнивать результаты каждые 100ms.

P.S. Обратите внимание: тесты для решения этой задачи используют dispatchEvent, чтобы проверить, что подсказка работает корректно.

Открыть решение с тестами в песочнице.