Урок"Умное" раскрывающееся вертикальное меню на jquery за 2 минуты

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

Допустим, у нас есть такое праймари меню:

  • Топ 100
  • Графика
    • Абстракция
    • Кино
    • Компьютерная графика

Задача.
При нажатии на пункт меню, который не имеет подменю, должна открываться новая страница (как в обычном меню). А если пункт меню имеет подменю, то страница не должна открываться, а должно выезжать подменю. Надо написать "умный" скрипт, который будет за этим следить.

Решение
Сначала выводим в нужное нам место праймари меню. Можно через блок, можно через page.tpl.php, кому как удобнее. Я обычно вывожу вторым способом:

<?php if ($primary_links): ?>
  <div id = "menu"><?php print menu_tree('primary-links'); ?></div>
<?php endif; ?>

В общем, вывели меню. Для него подойдёт следующий css:

div#menu ul {
  padding: 0;
}
 
div#menu ul li {
  display: block;
  border-bottom: 1px solid #211f1b;
  margin-bottom: 5px;
  padding: 0;
}
 
div#menu ul li a {
  color:#211F1B;
  display:block;
  font-family:Verdana;
  font-size:18px;	
  font-weight:bold;
  padding:5px 0;
}
 
div#menu ul li a:hover,
div#menu ul li a.active,
div#menu ul li a:active {
  color: #005693;
  text-decoration: none;	
  outline: none;
}
 
div#menu ul ul {
  display: none; /* собственно, самое важное :) */
}	
 
div#menu ul ul li {
  border: none;
  margin: 0;
}
 
div#menu ul ul li a {
  font-size: 11px;	
  padding-left: 20px;
}
 
div#menu ul ul li a:hover,
div#menu ul ul li.active-trail a {
  background: #b7b7b7;
  color: #fff;
}
 
div#menu ul ul li.active-trail ul li a {
  background: #fff;
  color: #000;
}

Теперь осталось создать скрипт, который будет раздвигать пункты меню, имеющие подменю. Создаём файл menu.js и добавляем туда такой текст:

$(document).ready(function() {
  $('div#menu>ul>li>a').click(function() {
    if ($(this).parent().find('ul').length) {
      $(this).parent().find('ul').slideToggle(200);				
      return false;
    }
  });
});

Подключаем наш скрипт в шаблон. Чистим кэш. Проверяем, наслаждаемся.

Таким же образом можно сделать и горизонтальное меню, слегка поменяв css.

Комментарии

Аватар пользователя D.Choomc
D.Choomc написал:

Спасибо Жека)
2 вопроса:
1. чем отличается твой вывод праймари в пэдж тпл пхп от такого:
echo theme('links', $primary_links);
Что более кошерно?

2. Я вот много видел уроков по созданию всплывающего меню. И постоянно все ругают разработчика, мол "чего ты через джейквери делаешь, зачем подключать их, ведь можно и просто на css сделать". Вопрос: объясни, когда можно и нужно включать js ))

ЗЫ: после друпал-пива у меня тяжко с составлением слов в фразы, а ты вон 2 статьи забацал))

24.10.2010 19:30
Аватар пользователя Spleshka
Spleshka написал:

1. Вариант, который написал ты, выводит только верхний уровень меню. А вариант, который я использовал в этой статье - всё меню с вложениями.
2. В друпале в стандартной сборке идёт библиотека Jquery, поэтому ничего подключать не надо. Пару строк кода - и всё работает)

25.10.2010 00:42
Аватар пользователя Vovovich
Vovovich написал:

SplasH, спасибо за мануал. Как раз стоит задача создать такое меню. Позже отпишусь о результатах.

31.10.2010 14:33
Аватар пользователя Spleshka
Spleshka написал:

Жду впечатлений :)

31.10.2010 14:38
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

Не работает, не раскрывается при наведении выпадающее меню

01.11.2010 00:12
Аватар пользователя Spleshka
Spleshka написал:

Оно должно работать не при наведении а при нажатии

01.11.2010 10:58
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

Ок, заработало. Вопрос - как сделать чтобы меню обратно сворачивалось при нажатии?

01.11.2010 07:36
Аватар пользователя Spleshka
Spleshka написал:

подменю будет обратно сворачиваться при нажатии на пункт, которым ты раскрыл подменю. В скрипте ведь указана функция slideToggle(200) - это значит, что оно прячется, если раскрыто, и раскрывается, если закрыто

01.11.2010 10:59
Аватар пользователя Vovovich
Vovovich написал:

Сделал все, как описано автором, даже стили не менял. В исходном коде страницы проверяю - элементы менюшек есть, но они при нажатии на родительский элемент не выползают. В чем может быть дело? Вот он этот сайт.

03.11.2010 02:18
Аватар пользователя Spleshka
Spleshka написал:

Дело в том, что у тебя нигде нет подменю. Чтобы выводилось подменю, надо перейти в раздел /admin/build/menu-customize/primary-links и у пунктов, имеющих подменю, поставить галочку напротив развёрнутое(expanded). Должно помочь

03.11.2010 13:01
Аватар пользователя Vovovich
Vovovich написал:

Поставил. Все-равно не разворачивает.

03.11.2010 13:50
Аватар пользователя Vovovich
Vovovich написал:

Разобрался!
Файл menu.js должен быть таким:

$(document).ready(function(){

$("div#menu>ul>li>a").click(function() {
if ($(this).parent().find("ul").length) {
$(this).parent().find("ul").slideToggle(200);
return false;
}
});

});

И тогда все работает. Иначе событие нажатия на нужный элемент не фиксировалось.

04.11.2010 16:41
Аватар пользователя Spleshka
Spleshka написал:

Да, конечно. Совсем забыл про этот пунктик. $(document).ready(function() вешает на выполнение скрипт после загрузке сайта, когда DOM будет готов.
Спасибо, поправил урок

04.11.2010 17:27
Аватар пользователя Vovovich
Vovovich написал:

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

05.11.2010 11:09
Аватар пользователя Spleshka
Spleshka написал:
$(document).ready(function(){
   $('div#menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);
          return false;
      }
   });
   $('div#menu li.active-trail ul').show();
});
05.11.2010 20:00
Аватар пользователя Vovovich
Vovovich написал:

SplasH, бесконечно благодарен вам!!! Теперь меню работает как нужно.

06.11.2010 03:21
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

При переходе по ссылке меню на новую страницу оно сворачивается. А как изменить код, чтобы сохранить развернутым подменю открытой страницы?

27.04.2011 11:20
Аватар пользователя Spleshka
Spleshka написал:
$('div#menu li.active-trail ul').show();

Вот эта строка сохраняет открытым подменю. Главное заменить #menu на айди вашего меню

27.04.2011 15:08
Аватар пользователя lomalkin.ru
lomalkin.ru написал:

Строил меню по аналогии вашему примеру, единственное отличие в моем случае - вложенность на 1 пункт больше, прикрутил стили по аналогии для 3го уровня, обнаружилось 2 проблемы:
1. При наведении на меню первого уровня, которое имеет дочерний элемент с дочернм 3м уровнем - 3й уровень тоже раскрывается, не знаю как победить, в стилях и jQuery настолько не силен.
2. В моем случае меню открывается при наведении. И если быстро провести по всем пунктам меню первого уровня, то можно наблюдать _все_ раскрытые меню, одновременно.
Если поможете победить - буду премного благодарен. Спасибо.

11.11.2010 19:23
Аватар пользователя Spleshka
Spleshka написал:

конечно можно победить, тут надо немного скрипт исправить. Если ты ещё не решил проблему - отпишись, я сделаю урок по трёхуровневому выпадающему меню (кстати, горизонтальное или вертикальное? )

14.11.2010 13:39
Аватар пользователя lomalkin.ru
lomalkin.ru написал:

Проблема решилась, меню переделывалось почти 5 раз и имеет теперь вложенность 4, работает видимо чудом =)
Но вы хорошо пишете, поэтому если напишите как правильно делать такие вещи, конечно же с удовольствием почитаю и приму к сведению.
В моем случае меню с первым горизонтальным уровнем и остальными вертикальными, выпадающими направо.

16.11.2010 22:19
Аватар пользователя Дима Богуш
Дима Богуш написал:

Большое спасибо... за скрипты, за помощь и общение. Конечно получилось не за 2 минуты, но получилось.

19.11.2010 16:17
Аватар пользователя Spleshka
Spleshka написал:

Дим, если бы ты скопировал на свой сайт меню из примера ничего не меняя - получилось бы 2 минуты :)

19.11.2010 16:59
Аватар пользователя Andresserg
Andresserg написал:

есть меню от ubercarta, выводиться в блок с айди дива block-uc_catalog-0, соотвественно в цсс я заменил menu на block-uc_catalog-0, в скрипте то же заменил, не знаю javascript, поэтому не могу разобраться, меню у меня формируются:

<div id="block-uc_catalog-0" class="block block-uc_catalog">
  <div class="content">
    <ul class="catalog menu">
		<li class="expanded">
			<span class="trail"><a href="/catalog/32" class="active">Ноутбуки (2)</a></span>
				<ul class="menu">
					<li class="expanded">
						<span class="trail"><a href="/catalog/36">Samsung (1)</a></span>
					</li>
					<li class="expanded">
						<span class="trail"><a href="/catalog/37">Sony (1)</a></span>
					</li>
 
				</ul>
		</li>
	</ul> 
  </div>
</div>

скрипте поменял menu на block-uc_catalog-0

$(document).ready(function(){
   $('div#block-uc_catalog-0>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);
          return false;
      }
   });
   $('div#block-uc_catalog-0 li.active-trail ul').show();
});

выводяться только первый уровень списка - Ноутбук, вторая категория не появляется, скрипт подцепляеться шаблоном.
понимаю что в вашем цсс надо мне заменить active-trail на что то своё, но не моогу разобраться, запутался, не прошу за меня что то делать, кто может объясните где возможная ошибка. Очень рад что зашел на ваш сайт, уроки очень классные уже изучаю. но хочеться с менюшкой тоже разобраться.

12.01.2011 10:29
Аватар пользователя Spleshka
Spleshka написал:

Так должно заработать:

$(document).ready(function(){
   $('div#block-uc_catalog-0>ul>li>span>a').click(function() {
       if ($(this).parent().parent().find('ul').length) {
          $(this).parent().parent().find('ul').slideToggle(200);
          return false;
      }
   });
 
   //li.active-trail - активный пункт меню. Класс автоматически друпалом вешается
   $('div#block-uc_catalog-0 li.active-trail ul').show();
 
   //Вместо предыдущей строки можно ещё так написать:
   $('div#block-uc_catalog-0>ul>li>span>a.active').parent().parent().find('ul').show();
 
});

Конструкция $('div#block-uc_catalog-0>ul>li>span>a') подразумевает, что следующим уровнем вложения после div#block-uc_catalog-0 будет ul, после ul будет li, после li будет span, а после span сразу a, и без дополнительных тэгов между ними

12.01.2011 14:47
Аватар пользователя Andresserg
Andresserg написал:

Сделал как ты сказал, но все равно не расскрывается, вот можешь посмотреть меню http://new22.ru/ ,может все таки что то не так в скрипте, очень уж хочеться такое меню, сейчас еще до views добрался)) интересная штука)

24.01.2011 19:23
Аватар пользователя Spleshka
Spleshka написал:

у тебя ul следует не сразу после block-uc_catalog-0, поэтому и не работает :) Вот так заработает 100%

$(document).ready(function(){
   $('div#block-uc_catalog-0>div.content>ul>li>span>a').click(function() {
       if ($(this).parent().parent().find('ul').length) {
          $(this).parent().parent().find('ul').slideToggle(200);
          return false;
      }
   });
 
   //li.active-trail - активный пункт меню. Класс автоматически друпалом вешается
 $('div#block-uc_catalog-0 li.active-trail ul').show();
 
   //Вместо предыдущей строки можно ещё так написать:
  $('div#block-uc_catalog-0>div.content>ul>li>span>a.active').parent().parent().find('ul').show();
 
});
24.01.2011 22:54
Аватар пользователя Andresserg
Andresserg написал:

СПАСИБО!!! :YAHOO: заработало! Понял свою ошибку, буду теперь с CSS разбираться)))

25.01.2011 18:46
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

Автор, неужели сложно, белять, сделать страницу с примером?

20.03.2011 20:37
Аватар пользователя powerscin
powerscin написал:

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

20.03.2011 22:33
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

А подскажите как сделать, чтобы не по клипу разворачивалось, а при наведении?

22.03.2011 13:32
Аватар пользователя powerscin
powerscin написал:

Достаточно тут заменить событие, было:

$(document).ready(function(){
   $('div#menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});

стало

$(document).ready(function(){
   $('div#menu>ul>li>a').mouseover(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});

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

$(document).ready(function(){
   $('div#menu>ul>li>a').mouseout(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});
22.03.2011 20:53
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

slideToggle тут совершенно не подходит, надо использовать slideDown, slideUp.

23.03.2011 10:24
Аватар пользователя powerscin
powerscin написал:

SlideToggle приводит к соврачиванию, если элемент развернут, и разворачиванию если элемент свернут. SlideToogle

23.03.2011 11:23
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

Нашел решение

$(document).ready(function(){
	var $ch = $('#content').height;
	var $sh = $('#sidebar').height;
	if ($ch>$sh) { $('#sidebar').css('height', $ch); };
	$('ul.menu').removeClass();
	$('div#menu>ul').find('ul').parent().each(function(i){
		$(this).hover(function(e){
			var $targetul=$(this).children('ul')
			if ($targetul.queue().length<=1)
				$targetul.slideDown(300)
			},
			function(e){
				$(this).children('ul').slideUp(200)
			}
		);
	});
});
23.03.2011 11:06
Аватар пользователя Лина
Лина написал:

Автору спасибо! Все работает)
У меня такой вопрос: а как можно сделать, чтоб меню при клике не раскрывалось/сворачивалось, а выпадало, то есть как бы поверх контента, как на facebook'e, при клике на кнопку Аккаунт. Или тут совсем другой подход нужен? Подскажите, плиз. Спасибо.

25.03.2011 16:35
Аватар пользователя Spleshka
Spleshka написал:

та не, тут всего лишь css надо поменять - и всё будет. Поиграться с абсолютным позиционированием

27.03.2011 04:01
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

А что, если при нажатии на родительский уровень надо, чтоб у него менялся бг (например стрелка вниз показывала по нажатию и соответсвенно оставалась в таком положении пока подменю развернуто)? Как это реализовать в скрипте?

29.04.2011 00:55
Аватар пользователя Spleshka
Spleshka написал:
$(document).ready(function(){
   $('div#menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).toggleClass('expanded-item');
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});

Теперь при нажатии на верхний пункт меню у него появится класс expanded-item, на который в css можно навесить бэкграундом стрелку вниз

29.04.2011 11:42
Аватар пользователя Alex
Alex написал:

Спасибо за урок! Очень помогло.
Только есть один вопрос, как сделать, чтобы при клике на не ракрытый пункт меню закрывался тот который уже был раскрыт?

16.05.2011 09:01
Аватар пользователя Spleshka
Spleshka написал:
$(document).ready(function(){
   $('div#menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().parent().find('li').close();
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});
16.05.2011 12:34
Аватар пользователя Alex
Alex написал:

Спасибо:HAPPY::HAPPY:

20.05.2011 15:24
Аватар пользователя Алексей
Алексей написал:

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

25.05.2011 15:25
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

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

$(document).ready(function(){
   $('div#block-menu-secondary-links>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);
          return false;
      }
   });
   $('div#block-menu-secondary-links li.active-trail ul').show();
});
13.06.2011 14:32
Аватар пользователя Spleshka
Spleshka написал:

Значит у вас этот скрипт не работает. Он не перезагружает страницу.

14.06.2011 20:35
Аватар пользователя Hudojnick
Hudojnick написал:

4to-to a ne ponyal kuda zapisyvat' v kakoi css fail?

28.07.2011 17:49
Аватар пользователя Deep
Deep написал:

Не работает js в Drupal7 не могли бы Вы помочь?
Пробовал с jQuery.noConflict

(function($){
$(document).ready(function(){
   $('div#my_menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().find('ul').slideToggle(200);
          return false;
      }
   });
});
})(jQuery);
21.08.2011 22:46
Аватар пользователя Ман Джу
Ман Джу написал:

Переименуйте все знаки ($) на jQuery

12.12.2011 18:34
Аватар пользователя Тайный поклонник
Тайный поклонник написал:

А как сделать не для $primary_links ,а для своего меню?

22.08.2011 12:10
Аватар пользователя Deep
Deep написал:

Этот код не работает

$(document).ready(function(){
   $('div#menu>ul>li>a').click(function() {
       if ($(this).parent().find('ul').length) {
          $(this).parent().parent().find('li').close();
          $(this).parent().find('ul').slideToggle(200);				
          return false;
      }
   });
});

Что это за функция?

$(this).parent().parent().find('li').close();

http://api.jquery.com/?ns0=1&s=close%28%29 нет такой функции... Автор отзовись!

22.08.2011 18:36
Аватар пользователя Spleshka
Spleshka написал:

Замените не работающую строку на эту:

$('div#menu > ul > li > ul').slideUp();
12.12.2011 20:49
Аватар пользователя Игорь
Игорь написал:

Здравствуйте!
А как вывести не основное меню (primary), а список разделов словаря(Таксономия), и чтоб он тоже раскрывался?

15.11.2011 17:48
Аватар пользователя Spleshka
Spleshka написал:

Поставьте модуль taxonomy_menu и сделайте из таксономии меню :)

12.12.2011 20:51
Аватар пользователя Vovan
Vovan написал:

Помогите разобраться, пожалуйста. У меня drupal 7. Не выводятся подменю. Поставить в настройках галочку "Show as expanded" не дает никакого результата. Нужно еще что-то где-то настроить? Или в чем может быть проблема?

26.03.2012 19:05
Аватар пользователя ice
ice написал:

Вы наверно пытаетесь проделать это с main-menu ?

Поискать не пробовали ?

Да простит меня splash , ссылка на блог конкурента )))..

26.03.2012 21:54
Аватар пользователя Vovan
Vovan написал:

Большое спасибо. Так все работает

27.03.2012 10:21
Аватар пользователя Дмитрий
Дмитрий написал:

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

$(function() {
 
	    var menu_ul = $('.vermenu > li > ul'),
	           menu_a  = $('.vermenu > li > a');
 
	    menu_ul.hide();
 
	    menu_a.click(function(e) {
	        e.preventDefault();
	        if(!$(this).hasClass('active')) {
	            menu_a.removeClass('active');
	            menu_ul.filter(':visible').slideUp('slow');
	            $(this).addClass('active').next().stop(true,true).slideDown('slow');
	        } else {
 
	            $(this).removeClass('active');
	            $(this).next().stop(true,true).slideUp('slow');
	        }
	    });
 
	});
15.08.2012 02:02
Аватар пользователя xSPiRiTx
xSPiRiTx написал:
<?php if ($primary_links): ?>
  <div id = "menu"><?php print menu_tree('primary-links'); ?></div>
<?php endif; ?>

а для Drupal 7 какой код использовать?

у меня выдает ошибку:

Notice: Undefined variable: primary_links в функции include() (строка 54 в файле /opt/lampp/htdocs/argamak/sites/all/themes/pixture_reloaded/templates/page.tpl.php).
30.08.2012 23:44
Аватар пользователя Виктория
Виктория написал:

Поставила модуль jquerymenu , все нормально работает, но при отключении java, оно не раскрывается, в итоге сделала в друпале показать раскрытым. При включении java, теперь все меню показывается раскрытым, а мне нужно, что бы список был раскрытым, только в случаи отключения java

24.09.2012 17:50
Аватар пользователя Андромеда
Андромеда написал:

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

06.03.2014 03:18
Аватар пользователя Андромеда
Андромеда написал:

Ах, простите, туплю, вопрос снимается!

06.03.2014 14:41
Аватар пользователя Tim
Tim написал:

А как дополнить функцией: Сворачивать неактивные пункты меню?? Может кто отзовется ?? Был бы оч. признателен

12.11.2016 11:46

Комментировать