Эволюция всегда идет по нарастающей. Вот и мы с каждой новой статьей продвигаемся в своих познаниях. С каждой новой строчкой меню становится все сложнее, но в то же время и красивее. В этот раз мы расширим границы возможностей и добавим JavaScript к нашему меню. Для чего нам вообще нужен JS. Обычно его добавляют для какой-нибудь анимации меню. Да, мы уже делали анимацию в теме Создание меню :: Горизонтальное :: Двухуровневое #example_4. Но вы могли заметить, что анимация работает только на появление меню. Кроме того далеко не все эффекты можно сделать лишь с CSS. Хоть нативные сайты с CSS-анимацией быстрее, чем с JS, но мы никогда не получим той гибкости и удобства без JS.
И так, поехали
В статье | Все статьи |
fadeIn / fadeOut
(example_1::github)
Для начала сделаем простую анимацию появления и исчезновения двухуровнего меню.
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
<div class="menu_top clearfix"> <ul> <li><a href="">Category 01</a> <ul> <li><a href="">Product 01 - 1</a></li> <li><a href="">Product 01 - 2</a></li> <li><a href="">Product 01 - 3</a></li> </ul> </li> <li><a href="">Category 02</a> <ul> <li><a href="">Product 02 - 1</a></li> <li><a href="">Product 02 - 2</a></li> <li><a href="">Product 02 - 3</a></li> <li><a href="">Product 02 - 4</a></li> </ul> </li> <li><a href="">Category 03</a> <ul> <li><a href="">Product 03 - 1</a></li> <li><a href="">Product 03 - 2</a></li> </ul> </li> <li><a href="">Category 04</a> <ul> <li><a href="">Product 04 - 1</a></li> <li><a href="">Product 04 - 2</a></li> <li><a href="">Product 04 - 3</a></li> <li><a href="">Product 04 - 4</a></li> <li><a href="">Product 04 - 5</a></li> </ul> </li> <li><a href="">Category 05</a></li> <li><a href="">Category 06</a></li> <li><a href="">Category 07</a> <ul> <li><a href="">Product 07 - 1</a></li> <li><a href="">Product 07 - 2</a></li> </ul> </li> <li><a href="">Category 08</a></li> <li><a href="">Category 09</a></li> </ul> </div> |
Ничего нового в разметке нету. CSS стал даже проще, чем был (сравните с Создание меню :: Горизонтальное :: Двухуровневое #example_2)
CSS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
.menu_top > ul > li { /* 1-ый уровень */ float: left; padding: 5px; border: 1px black solid; } .menu_top > ul > li > ul { /* 2-ой уровень */ display: none; position: absolute; /* Если не будет absolute, то все элементы будут строиться внутри существующего */ z-index: 1; } .menu_top > ul > li:hover > ul { /*display: block;*/ /* CHANGED */ } |
Как видите я закомментировал строку, где меню должно появится. Теперь за этим свойством будет следить JS
JS (в своих примерах я использую jQuery, для любителей нативного JS рекомендую посмотреть на VanillaJS)
1 2 3 4 5 6 7 8 9 10 11 12 |
$(function() { $('.menu_top ul li').hover( /* Функция исполняется, когда мышь входит в область меню */ function mouseOver() { $('> ul',this).fadeIn(1000); }, /* Функция исполняется, когда мышь покидает область меню */ function mouseOut() { $('> ul',this).fadeOut(1000); } ) }); |
Кратко поясню: на каждый элемент li вешаются 2 обработчика mouseover(1-ая функция) и mouseout (2-ая функция). В каждой из них мы находим дочернее меню (‘> ul’) и выполняем функцию появления (fadeIn) или исчезновения (fadeOut). Числа, переданные в качетве параметра, – это время анимации в миллисекундах.
setTimer
(example_2::github)
Усложним пример. Сделаем анимацию с задержкой на многоуровневом меню. Если Вы разберетесь в этой теме, то Вы разберетесь во всем.
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
<div class="menu_top clearfix"> <ul> <!-- Пункт меню 1 --> <li><a href="#">Пункт 1</a> <ul> <li><a href="#">подпункт 1.1</a></li> <li><a href="#">подпункт 1.2</a> <ul> <li><a href="#"> подпункт 1.2.1 </a> <ul> <li><a href="#"> подпункт 1.2.1.1 </a></li> <li><a href="#"> подпункт 1.2.1.2</a> <ul> <li><a href="#"> подпункт 1.2.1.2.1 </a></li> <li><a href="#"> подпункт 1.2.1.2.2</a></li> <li><a href="#"> подпункт 1.2.1.2.3</a></li> <li><a href="#"> подпункт 1.2.1.2.4</a></li> <li><a href="#"> подпункт 1.2.1.2.5</a></li> </ul> </li> <li><a href="#"> подпункт 1.2.1.3</a></li> <li><a href="#"> подпункт 1.2.1.4</a></li> <li><a href="#"> подпункт 1.2.1.5</a></li> </ul> </li> <li><a href="#"> подпункт 1.2.2</a> <ul> <li><a href="#"> подпункт 1.2.2.1 </a></li> <li><a href="#"> подпункт 1.2.2.2 </a></li> <li><a href="#"> подпункт 1.2.2.3 </a></li> <li><a href="#"> подпункт 1.2.2.4 </a></li> <li><a href="#"> подпункт 1.2.2.5 </a></li> </ul> </li> <li><a href="#"> подпункт 1.2.3</a></li> <li><a href="#"> подпункт 1.2.4</a></li> <li><a href="#"> подпункт 1.2.5</a> <ul> <li><a href="#"> подпункт 1.2.5.1 </a></li> <li><a href="#"> подпункт 1.2.5.2</a></li> <li><a href="#"> подпункт 1.2.5.3</a></li> <li><a href="#"> подпункт 1.2.5.4</a></li> <li><a href="#"> подпункт 1.2.5.5</a></li> </ul> </li> </ul> </li> <li><a href="#">подпункт 1.3</a> <ul> <li><a href="#"> подпункт 1.3.1 </a></li> <li><a href="#"> подпункт 1.3.2 </a></li> <li><a href="#"> подпункт 1.3.3 </a></li> <li><a href="#"> подпункт 1.3.4 </a></li> <li><a href="#"> подпункт 1.3.5 </a></li> </ul> </li> <li><a href="#">подпункт 1.4</a></li> <li><a href="#">подпункт 1.5</a> <ul> <li><a href="#">подпункт 1.5.1 </a></li> <li><a href="#"> подпункт 1.5.2</a></li> <li><a href="#"> подпункт 1.5.3</a></li> <li><a href="#"> подпункт 1.5.4</a></li> <li><a href="#"> подпункт 1.5.5</a></li> </ul> </li> </ul> </li> <!-- Пункт меню 2 --> <li><a href="#">Пункт 2</a> <ul> <li><a href="#">подпункт 2.1</a> <ul> <li><a href="#"> подпункт 2.1.1 </a> <ul> <li><a href="#"> подпункт 2.2.1.1 </a></li> <li><a href="#"> подпункт 2.2.2.2 </a></li> <li><a href="#"> подпункт 2.2.3.3 </a></li> <li><a href="#"> подпункт 2.2.4.4 </a></li> <li><a href="#"> подпункт 2.2.5.5 </a></li> </ul> </li> <li><a href="#"> подпункт 2.1.2</a></li> <li><a href="#"> подпункт 2.1.3</a></li> <li><a href="#"> подпункт 2.1.4</a></li> <li><a href="#"> подпункт 2.1.5</a></li> </ul> </li> <li><a href="#">подпункт 2.2</a></li> <li><a href="#">подпункт 2.3</a></li> <li><a href="#">подпункт 2.4</a></li> <li><a href="#">Длинный подпункт, длинный подпункт</a></li> </ul> </li> <!-- Пункт меню 3 --> <li><a href="#">Длинный пункт меню 3</a> <ul> <li><a href="#">подпункт 3.1</a> <ul> <li><a href="#"> подпункт 3.1.1 </a></li> <li><a href="#"> подпункт 3.1.2 </a></li> <li><a href="#"> подпункт 3.1.3 </a></li> <li><a href="#"> подпункт 3.1.4 </a></li> <li><a href="#"> подпункт 3.1.5 </a></li> </ul> </li> <li><a href="#">подпункт 3.2</a> <ul> <li><a href="#"> подпункт 3.2.1 </a></li> <li><a href="#"> подпункт 3.2.2 </a></li> <li><a href="#"> подпункт 3.2.3 </a></li> <li><a href="#"> подпункт 3.2.4 </a></li> <li><a href="#"> подпункт 3.2.5 </a></li> </ul> </li> <li><a href="#">подпункт 3.3</a></li> <li><a href="#">подпункт 3.4</a></li> <li><a href="#">подпункт 3.5</a></li> </ul> </li> <!-- Пункт меню 4 --> <li><a href="#">Пункт 4</a> <ul> <li><a href="#">подпункт 4.1</a> <ul> <li><a href="#"> подпункт 4.1.1 </a></li> <li><a href="#"> подпункт 4.1.2 </a></li> <li><a href="#"> подпункт 4.1.3 </a></li> <li><a href="#"> подпункт 4.1.4 </a></li> <li><a href="#"> подпункт 4.1.5 </a></li> </ul> </li> <li><a href="#">подпункт 4.2</a></li> <li><a href="#">подпункт 4.3</a> <ul> <li><a href="#"> подпункт 4.3.1 </a></li> <li><a href="#"> подпункт 4.3.2 </a></li> <li><a href="#"> подпункт 4.3.3 </a></li> <li><a href="#"> подпункт 4.3.4 </a></li> <li><a href="#"> подпункт 4.3.5 </a></li> </ul> </li> <li><a href="#">подпункт 4.4</a></li> <li><a href="#">подпункт 4.5</a></li> </ul> </li> <!-- Пункт меню 5 --> <li><a href="#">Пункт 5</a> <ul> <li><a href="#">подпункт 5.1</a> <ul> <li><a href="#"> подпункт 5.1.1 </a></li> <li><a href="#"> подпункт 5.1.2 </a></li> <li><a href="#"> подпункт 5.1.3 </a></li> <li><a href="#"> подпункт 5.1.4 </a></li> <li><a href="#"> подпункт 5.1.5 </a></li> </ul> </li> <li><a href="#">подпункт 5.2</a></li> <li><a href="#">подпункт 5.3</a></li> <li><a href="#">подпункт 5.4</a></li> <li><a href="#">подпункт 5.5</a></li> </ul> </li> </ul> <!-- Конец списка --> </div> |
CSS (пример взят из Создание меню :: Горизонтальное :: Многоуровневое меню #example_1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
.menu_top > ul > li { /* 1 уровень */ float: left; border: 1px solid #222; } .menu_top > ul li ul { /* Прячем все меню ниже 1 уровня */ display: none; position: absolute; } .menu_top > ul li:hover > ul { /* Показываем все меню при наведении на родительский li, которые находятся ниже 1 уровня*/ /*display: block;*/ /* CHANGED */ } .menu_top ul li { /* NEW */ position: relative; /* Для того чтобы top:0 для меню >=3 уровня выравнивал по li, а не по ul т.е. если закомментировать то все подменю (начиная со 2-го уровня) будут выстроены в одну линию по верхнему краю*/ } .menu_top > ul ul ul { /* Начиная с 3-го уровня размещать списки справа от родительского li */ /* Начиная с 3-го уровня размещать списки справа от родительского li */ top: 0; left: 100%; } |
Опять за появление отвечает JS
JS в этот раз поинтереснее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$(function() { $('.menu_top ul li').hover( /* Функция исполняется, когда мышь входит в область меню */ function mouseOver() { clearTimeout($.data(this, 'timer')); /* если мы успели вернуться мышкой на меню, до того как прошла задержка на закрытие, то таймер обнуляется и сворачивание меню не произойдет*/ /* если использовать stop(true, FALSE), то скачка на конец анимации не будет (false стоит по умолчанию) */ $(this).find('> ul').stop(true).slideDown(1000); /* анимация разворачивания меню */ }, /* Функция исполняется, когда мышь покидает область меню */ function mouseOut() { var $this = $(this); /* делаем замыкание, т.к. в Таймере до объекта мы не достучимся*/ $.data(this, 'timer', setTimeout( /* Устанавливаем таймер, чтобы закрытие меню не начиналось сразу после того как мышь покинет li */ function () { $this.find('> ul').stop(true).slideUp(1000); /* анимация сворачивания меню */ }, 200) /* задержка закрытия меню */ ); }); }); |
Мы опять используем 2 функции. Самое интересное происходит внутри функций. Чтобы лучше разобраться начнем с конца
MouseOut:
- В переменной $this запоминаем элемент li, с которым происходит событие (мышка покинула область li)
- В разметки для элемента li добавляем псевдо аттрибут data-timer с параметром ID таймера (почему псевдо – потому что data привязывает не к объекту DOM, а к объекты jQuery. Он будет сохранен внутри jQuery)
- Создаем таймер с задержкой 200 мс
- Когда пройдет 200мс начнет выполняться функция внутри таймера.
- При помощи замыкания передаем элемент li
- Находим дочернее меню
- Останавливаем все анимации для этого элемента, чтобы они не накладывались
- Сворачиваем меню
MouseOver:
Тут еще проще
- Ищем аттрибут data-timer у элемента li (если он есть, то мы получим ID таймера закрытия меню)
- Останавливаем таймер
- Опять ищем дочернее меню, останавливаем анимации и запускаем анимацию открытия
Зачем такие сложности? Благодаря задержки в 200мс, у пользователя не закроется меню если он, случайно вышел за пределы меню на время меньшее 200мс.