Post picture

Hover-intent в переводе на русский язык означает — зависание при наведении. В данном контексте подразумевается поведение элемента страницы, в случае, когда курсор мыши находиться над ним, либо покидает его. Данная функция существует в библиотеке JQuery уже готовая, но что делать если мы не хотим нагромождать лишний код и подключать сторонние библиотеки? В этом случае можете написать собственную функцию, которая будет соответствовать вашим потребностям. В данной статье мы создадим собственную функцию HoverIntentToggleClass. Вы можете спросить, зачем тратить свое драгоценное, время на создание подобных функций? Ведь существуют готовые функции, которые написаны профессионалами. Да, это так ! Но ведь мы собрались здесь не для того, чтобы выбирать лёгкие пути, а для того чтобы творить и постигать основы программирования, на конкретных примерах.
Наша функция, по возможностям, похожа на подобные функции, но все все же имеет одну особенность! Особенной её делает то, что она заточена для работы с меню. Стандартная функция JQuery — замечательная! Она написана профессионалами, но она не имеет того функционала, который нужен мне. А наша функция, на мой взгляд очень проста и уникальна.

Функция HoverIntentToggleClass


Возможности функции HoverIntentToggleClass


  • функция представлена в виде класса
  • присутствует задержка, в поведении элемента, при наведении курсора мыши,
  • присутствует задержка, в поведении элемента, когда курсор покинул элемент,
  • присутствуют две CallBack функции(callBackOver, callBackLeave)
  • мгновенное обнуление класса(без задержки), при переходе на другой пункт меню(чего нет у других)

И так перейдем непосредственно к скрипту. Скрипт состоит из функции в виде класса HoverIntentToggleClass. С HoverIntent всё понятно ! А вот ToggleClass, в названии функции, обозначает, что при наведении курсора на элемент, по истечению установленной задержки, к нему будет прикреплен активный класс стилей, а после того как курсор мыши покинет элемент меню, класс будет удалён.
Это всего лишь название, но название должно говорить само за себя! Делается это для того, чтобы было предельно понятно, что делает функция. Возьмем её верхнюю часть, с аргументами и свойствами, и разберем что тут и зачем:

class HoverIntentToggleClass {
	constructor(activeClass, parentNode, childNode, pauseOver, pauseLeave, callBackOver, callBackOverArg, callBackLeave, callBackLeaveArg) {
		this.activeClass = activeClass;
		this.timerOver;
		this.timerOut;
		this.elements = document.querySelector(parentNode).querySelectorAll(childNode);
		this.pauseOver = pauseOver;
		this.pauseLeave = pauseLeave;
		this.prevElement;
		this.callBackOverArg = callBackOverArg;
		this.callBackLeaveArg = callBackLeaveArg;
		this.callBackOver = callBackOver;
		this.callBackLeave = callBackLeave;
	}
}

Аргументы функции HoverIntentToggleClass


  • activeClass — это класс, который вы будете использовать, для управления видимостью элементов меню
  • parentNode — это родительский узел нашего меню, в нашем случае идентификатор элемента UL
  • childNode — это дочерние элементы, с которыми будет работать скрипт, для нас это дочерние LI первого колена
  • pauseOver — это задержка (м\с), при наведении на элемент, по истечению которой к элементу будет прикреплен определенный класс
  • pauseOut — это задержка (м\с), когда курсор покидает элемент, по истечению которой у элемента будет откреплен класс
  • callBackOver — название возвратной функции при наведении курсора на элемент
  • callBackOverArg — аргумент для функции callBackOver, если аргументов несколько то передавать в виде объекта
  • callBackLeave — название возвратной функции когда курсор покидает элемент
  • callBackLeaveArg — аргумент для функции callBackLeave, если аргументов несколько то передавать в виде объекта

Свойства функции HoverIntentToggleClass


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

  • this.timerOver — объявляем переменную в которой будет храниться идентификатор таймера(setTimeout), после того как курсор мыши оказался над элементом
  • this.timerOut — объявляем переменную в которой будет храниться идентификатор таймера(setTimeout), после того как курсор мыши покинул элемент
  • this.elements — коллекция элементов с которыми будет взаимодействовать функция
  • this.prevElement — переменная в которой при переходе на другой элемент, будет сохраняться предыдущий элемент. Нужна она для мгновенного обнуления активного класса для предыдущего элемента, при переходе на другой.

Методу overLeave()


У нашей функции всего два метода ! В методе overLeave() заключена основная программа, а метод init() является, так сказать, служебным(для инициализации). Так что поговорим только о первом методе.

overLeave() {
		let pauseOver = this.pauseOver;
		let pauseOut = this.pauseOut;
		let prevElement = this.prevElement;
		let callBackOn = this.callBackOver; 
		let callBackOut = this.callBackLeave; 
		let overArg = this.callBackOverArg;
		let leaveArg = this.callBackLeaveArg;
        
       	// --------------- цикл ----------
		this.elements.forEach(function (element) {

			// --------------- Когда курсор над элементом ----------
			element.addEventListener('mouseenter', function (event) {
				event.stopPropagation()			
				clearTimeout(this.timerOut)
				if (element && element !== prevElement && prevElement !== undefined) prevElement.classList.remove('active')
				this.timerOver = setTimeout(function () {
					if (!element.classList.contains('active')) element.classList.add('active');	
					if(callBackOn)callBackOn(overArg);			
				}, pauseOver);
			});

			// --------------- Когда курсор вне элемента ----------
			element.addEventListener('mouseleave', function (event) {
				event.stopPropagation()
				clearTimeout(this.timerOver)
				this.timerOut = setTimeout(function () {
					if (element.classList.contains('active')) element.classList.remove('active');	
					if(callBackOut)callBackOut(leaveArg);					
				}, pauseOut);
				if (element) prevElement = element
			});
		});
	}

Сначала мы видим, что происходит присваивание переменных внутри метода. Делается это для того, чтобы все переменные попали лингвистическое окружение нашего метода и не возникало никаких ошибок при работе метода. Затем с помощью цикла, скрипт устанавливает отслеживание событий mouseenter и mouseleave для каждого элемента из коллекции this.elements.

Рассмотрим тело функции, которая отрабатывает при событии mouseenter:


  • event.stopPropagation() — нужен для того, чтобы событие не погружалось далее по DOM, а остановилось на нашем элементе.
  • clearTimeout(this.timerOut) — нужен для того, чтобы остановить таймер(setTimeout) для элемента который был покинут.
  • if (element && element !== prevElement && prevElement !== undefined) prevElement.classList.remove(‘active’) — логика для мгновенного обнуления активного класса, при переходе на другой элемент.
  • this.timerOver = setTimeout(function () {—тело функции—}, pauseOver) — присваиваем переменной идентификатор таймера при наведении курсора на элемент.
  • if (!element.classList.contains(‘active’)) element.classList.add(‘active’) — логика, если элемент не содержит активный класс, тогда добавить его.
  • if(callBackOn)callBackOn(overArg) — если существует обратная функция при наведении, то исполнить ее.

Рассмотрим тело функции, которая отрабатывает при событии mouseleave:


  • event.stopPropagation() — аналогично для mouseenter
  • clearTimeout(this.timerOver) — чтобы остановить таймер(setTimeout) для элемента при наведении курсора.
  • this.timerOut = setTimeout(function () {—тело функции—}, pauseOut) — присваиваем переменной идентификатор таймера когда курсор покинул элемент.
  • if (element.classList.contains(‘active’)) element.classList.remove(‘active’) — логика, теперь если элемент содержит активный класс, тогда убираем его.
  • if(callBackOut)callBackOut(leaveArg); — если существует обратная функция когда курсор покидает элемент, то исполнить ее.
  • if (element) prevElement = element; — сохранение текущего элемента в переменную предыдущего элемента

Код функции HoverIntentToggleClass


Ну теперь нам осталось лишь, инициализировать наш метод, и данный класс можно считать законченным. Давайте соберем все наши кусочки кода в один и посмотрим, что же у нас получилось:

class HoverIntentToggleClass {
	constructor(activeClass, parentNode, childNode, pauseOver, pauseLeave, callBackOver, callBackOverArg, callBackLeave, callBackLeaveArg) {
		this.activeClass = activeClass;
		this.timerOver;
		this.timerOut;
		this.elements = document.querySelector(parentNode).querySelectorAll(childNode);
		this.pauseOver = pauseOver;
		this.pauseLeave = pauseLeave;
		this.prevElement;
		this.callBackOverArg = callBackOverArg;
		this.callBackLeaveArg = callBackLeaveArg;
		this.callBackOver = callBackOver;
		this.callBackLeave = callBackLeave;
	}
	overLeave() {
		let activeClass = this.activeClass;
		let pauseOver = this.pauseOver;
		let pauseLeave = this.pauseLeave;
		let prevElement = this.prevElement;
		let callBackOn = this.callBackOver;
		let callBackOut = this.callBackLeave;
		let overArg = this.callBackOverArg;
		let leaveArg = this.callBackLeaveArg;

		this.elements.forEach(function (element) {

			// --------------- Когда курсор над пунктом ----------
			element.addEventListener('mouseenter', function (event) {
				event.stopPropagation()
				clearTimeout(this.timerOut)
				if (element && element !== prevElement && prevElement !== undefined) prevElement.classList.remove(activeClass)
				this.timerOver = setTimeout(function () {
					if (!element.classList.contains(activeClass)) element.classList.add(activeClass);
					if (callBackOn) callBackOn(overArg);
				}, pauseOver);
			});

			// --------------- Когда курсор вне пункта ----------
			element.addEventListener('mouseleave', function (event) {
				event.stopPropagation()
				clearTimeout(this.timerOver)
				this.timerOut = setTimeout(function () {
					if (element.classList.contains(activeClass)) element.classList.remove(activeClass);
					if (callBackOut) callBackOut(leaveArg);
				}, pauseLeave);
				if (element) prevElement = element
			});
		});
	}
   // --------------- инициализируем метод ----------
	init() {
		this.overLeave();
	}
}

Пример меню, с использованием функции.


Основную задачу по созданию hover-intent мы сделали, и это отличная работа ! Но теперь необходимо как то запустить всё это и оценить на реальном примере. Для начала, создадим файл index.html, в котором сделаем разметку меню. Теперь добавим немного стилей, чтобы наше меню имело правильный вид. Подробно рассматривать создание разметки и стилей, мы не будем, так как сегодняшняя статья, скорее о JavaScript, чем о верстке. Ну и конечно же, мы не забудем подключить и инициализировать нашу функцию.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hover-Intent</title>

</head>
<style>
    .container {
        max-width: 1200px;
        padding-left: 1rem;
        padding-right: 1rem;
        margin: 0 auto;
    }
    ul {
        list-style: none;
        margin: 0;
        padding: 1rem;
    }
    ul a{
        text-decoration: none;        
    }
    nav {
        width: 100%;
        background-color: rgb(74, 73, 73);
    }
    .menu {
        display: inline-flex;
        align-items: center;
        justify-content: start;
        gap: 0.5rem;
    }

    .menu>li {
        position: relative;
        padding: 0.5rem;
    }

    .menu>li>a {
        text-transform: uppercase;
        color: white;     
    }

    .menu>li.active>a {
        color: grey;
    }

    .menu>li>.sub-menu {
        display: none;
        position: absolute;
        top: 100%;
        padding: 1rem;
        background-color: rgb(74, 73, 73);
    }

    .menu>li.active>.sub-menu {
        display: block;
    }
    .sub-menu a {
        color: white;        
    }

    .sub-menu a:hover {
        color: grey;
    }
</style>

<body>
    <div class="container">
        <nav class="navigation">
            <ul class="menu">
                <li><a href="#">Главная</a></li>
                <li>
                    <a href="#">Выпадающее</a>
                    <ul class="sub-menu">
                        <li><a href="#">Пункт меню 1</a></li>
                        <li><a href="#">Пункт меню 2</a></li>
                        <li><a href="#">Пункт меню 3</a></li>
                        <li><a href="#">Пункт меню 4</a></li>
                    </ul>
                </li>
                <li><a href="#">Наши работы</a></li>
                <li><a href="#">Викторина</a></li>
                <li><a href="#">Контакты</a></li>
            </ul>
        </nav>
    </div>
    <script>
        class HoverIntentToggleClass {
            constructor(activeClass, parentNode, childNode, pauseOver, pauseLeave, callBackOver, callBackOverArg, callBackLeave, callBackLeaveArg) {
                this.activeClass = activeClass;
                this.timerOver;
                this.timerOut;
                this.elements = document.querySelector(parentNode).querySelectorAll(childNode);
                this.pauseOver = pauseOver;
                this.pauseLeave = pauseLeave;
                this.prevElement;
                this.callBackOverArg = callBackOverArg;
                this.callBackLeaveArg = callBackLeaveArg;
                this.callBackOver = callBackOver;
                this.callBackLeave = callBackLeave;
            }
            overLeave() {
                let activeClass = this.activeClass;
                let pauseOver = this.pauseOver;
                let pauseLeave = this.pauseLeave;
                let prevElement = this.prevElement;
                let callBackOn = this.callBackOver;
                let callBackOut = this.callBackLeave;
                let overArg = this.callBackOverArg;
                let leaveArg = this.callBackLeaveArg;

                this.elements.forEach(function (element) {

                    // --------------- Когда курсор над пунктом ----------
                    element.addEventListener('mouseenter', function (event) {
                        event.stopPropagation()
                        clearTimeout(this.timerOut)
                        if (element && element !== prevElement && prevElement !== undefined) prevElement.classList.remove(activeClass)
                        this.timerOver = setTimeout(function () {
                            if (!element.classList.contains(activeClass)) element.classList.add(activeClass);
                            if (callBackOn) callBackOn(overArg);
                        }, pauseOver);
                    });

                    // --------------- Когда курсор вне пункта ----------
                    element.addEventListener('mouseleave', function (event) {
                        event.stopPropagation()
                        clearTimeout(this.timerOver)
                        this.timerOut = setTimeout(function () {
                            if (element.classList.contains(activeClass)) element.classList.remove(activeClass);
                            if (callBackOut) callBackOut(leaveArg);
                        }, pauseLeave);
                        if (element) prevElement = element
                    });
                });
            }
            init() {
                this.overLeave();
            }
        }
       // создаём объект hoverIntent, путем присваивания функции   
        let hoverIntent = new HoverIntentToggleClass(
            'active',                             //  название активного класса    
            '.menu',                            //  родительский узел
            '.menu>li',                       //  отслеживаем только дочерние элементы первого колена, в родительском узле
            300,                                 //  задержка при наведении курсора
            500,                                 //  задержка когда курсор покинул элемент
            doOver,                           //  callBackOver - возвратная функция при наведении
            'курсор над элементом',  //  callBackOverArg - аргумент к функции при наведении
            doLeave,                         //  callBackLeave - возвратная функция курсор покинул элемент
            'курсор покинул элемент' //  callBackLeaveArg - аргумент к функции при выходе
        )
        hoverIntent.init()

        function doOver(callBackOverArg) {
            console.log(callBackOverArg);            
        }
        function doLeave(callBackLeaveArg) {
            console.log(callBackLeaveArg);
        }
    </script>
</body>

</html>

PostScriptum: функции doOver и doLeave в данном примере, не выполняют ничего, кроме вывода сообщения о событии в консоль. Сделано это для демонстрации работоспособности обратных функций, для каждого события. Если вам не нужны callback функции, можете исключить их из кода. В этом случае не забудьте убрать соответствующие аргументы из объекта hoverIntent.

Всем добра ! Оставляйте ваши комментарии.

 DEMO

WebLegko

Здесь мы занимаемся изучением основ веб-программирования. Присоединяйтесь !

Добавить комментарий