Оценок: 1
Горизонтальное меню на основе списка

Горизонтальное меню на основе списка

Здесь рассмотрим создание адаптивного меню, на основе списка. Для desktop версии меню будет горизонтальным, для мобильной вертикальным. Раскрытие самого меню и пунктов в мобильной версии через JQuery, все остальное - HTML и CSS.

Раскрытие будет плавным

Что бы было интереснее и полезнее (а еще потому, что сайт на чистом HTML сейчас большая редкость) добавим получение пунктов меню из БД, используя PHP, покажу весь процесс превращения данных из таблицы БД в меню сайта. Кому не интересно, можно просто пролистать дальше.

В самом конце есть рабочий пример

База данных

Все пункты меню изначально представлю в виде таблицы, подобно тому, как они хранятся в MySql (Один из вариантов).

Таблица будет называться "menu". Столбцы: menu_id (уникальный порядковый номер), name (название пункта меню), link (ссылка), parent (родитель)

menu_idnamelinkparent
0Пункт 1#0
1Пункт 2#0
2Пункт 3#0
3Пункт 4#0
4Подпункт 1#1
5Подпункт 2#1
6Подпункт 3#1
7Подпункт 4#3
8Подпункт 5#3
9Подпункт 6#3
10Подпункт 7#3
11Подпункт 8#3

А теперь займемся выводом.

PHP. Получение пунктов, подготовка к выводу в шаблон

Т.к. цель - получить меню на сайте, а не научится работать с БД MySQL, здесь немного упрощу, иначе нужно будет начинать с подключения к MySQL и оттуда двигаться дальше, а учитывая что здесь могут быть варианты, рискую запутать.

Для начала запрос к БД для получения пунктов, который будет находится в функции getMenu($parent) {}. Именно к фунции с таким именем будем дальше обращаться, функция будет получать родителя в переменной "$parent" и возвращать массив с пунктами. Модель, MySQL запрос:

SELECT * FROM menu WHERE `parent` = '" . (int)$parent . "'

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

$menu = array();//для начала объявим переменную с пустым массивом, на случай если в таблице ничего не найдется.
$q_menu = getMenu(0); //получаем массив с корневыми пунктами и затем перебираем его
foreach ($q_menu as $menu_item) {
	$childs = array(); //пустой массив с подпунктами, опять же на случай, если таковых нет.
	$q_childs = getMenu($menu_item['menu_id']);//Получаем подпункты для текущего пункта меню. Здесь текущий пункт - родитель. Перебираем их.
	foreach ($q_childs as $child_item) {
		//формируем массив с подпунктами
		$childs[] = array(
			'menu_id' => $child_item['menu_id'],
			'name' => $child_item['name'],
			'link' => $child_item['link']
		);
	}
	//Теперь формируем массив с корневыми пунктами, включающий массивы подпунктом (многомерный массив всего меню)
	$menu[] = array(
		'menu_id' => $child_item['menu_id'],
		'name' => $child_item['name'],
		'link' => $child_item['link'],
		'childs' => $childs
	);
}

Теперь можно переходить к выводу в шаблон

HTML. Вывод в шаблоне. И немного PHP

Для начала вывод с помощью PHP

<span class="menu-toggle"><img src="images/menu/menu40.png" alt="Показать меню" /></span><nav id="top-menu" class="top-menu">
	<ul class="parent">
	<?php foreach ($menu as $menu_item) { ?>
		<?php if ($menu_item['childs']) { ?>
			<li><a href="<?php echo $menu_item['link']; ?>"><?php echo $menu_item['name']; ?></a>
				<span class="togg"></span>
				<div class="child">
					<ul>
				<?php foreach ($menu_item['childs'] as $child_item) { ?>
					<li><a href="<?php echo $child_item['link']; ?>"><?php echo $child_item['name']; ?></a></li>
				<?php } ?>
					</ul>
				</div>
			</li>
		<?php } else { ?>
			<li><a href="<?php echo $menu_item['link']; ?>"><?php echo $menu_item['name']; ?></a></li>
		<?php } ?>
	<?php } ?>
	</ul>
</nav>

В результате получим вот такой HTML:

<span class="menu-toggle"><svg viewBox="0 0 448 512" width="100" title="bars"><path d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z" /></svg></span>
<nav id="top-menu" class="top-menu">
	<ul class="parent">
		<li><a href="#">Пункт 1</a>
			<span class="togg"></span>
			<div class="child">
				<ul>
					<li><a href="#">Подпункт 1</a></li>
					<li><a href="#">Подпункт 2</a></li>
					<li><a href="#">Подпункт 3</a></li>
				</ul>
			</div>
		</li>
		<li><a href="#">Пункт 2</a></li>
		<li><a href="#">Пункт 3</a>
			<span class="togg"></span>
			<div class="child">
				<ul>
					<li><a href="#">Подпункт 4</a></li>
					<li><a href="#">Подпункт 5</a></li>
					<li><a href="#">Подпункт 6</a></li>
					<li><a href="#">Подпункт 7</a></li>
					<li><a href="#">Подпункт 8</a></li>
				</ul>
			</div>
		</li>
		<li><a href="#">Пункт 4</a></li>
	</ul>
</nav>

Подпункты обернуты сначала в контейнер .child, затем в ul. Сделано так, что бы визуально сделать отступ между основным меню и всплывающими пунктами при наведении.

Тепеерь займемся оформлением и для начала напишем небольшой скрипт для раскрытия меню, проверим что меню скрывается и раскрывается, затем CSS.

Оформление JS и CSS

Скрипт jQuery открытия/закрытия меню:

$(".menu-toggle").click(function () {
	$('#top-menu').toggle().toggleClass('open');
	$('body').toggleClass('menuopen')
});
/*Для раскрытия внутренних пунктов, мобильное меню*/
$('.togg').click(function () {
    if ($(this).hasClass('active')) {
        $(this).removeClass('active').next().removeClass('open');
    } else {
        $(this).addClass('active').next().addClass('open');
    }
});

Добавление класса "menuopen" для body нужно, что бы фиксировать страницу при раскрытии меню в мобильной версии (прокручиваться должно только меню, а не вся страница).

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


/*Скроем кнопку для desktop*/
.menu-toggle {display:none;}
.top-menu ul {
  list-style-type: none;
  padding-left: 0px;
  margin: 0px;
}
.top-menu ul li {position: relative;}
.top-menu .child {
  background: rgba(255, 255, 255, 0.4);
  border-radius:0px 0px 5px 5px;
  /*Следующим способом можно добиться плавного раскрытия списка.
  Ограничить высоту = 0 в неактивном состоянии и максимально возможным значением в активном.
  Так же нужно добавить плавный переход (ниже)*/
  max-height: 0px;
  overflow: hidden;
}
.top-menu .child ul {
  background: rgba(255, 255, 255, 0.7);
  min-width: 220px;
  box-shadow: 0px 2px 5px #a7a7a7;
  border-radius: 0px 0px 5px 5px;
  margin:10px 4px 4px;
}
.top-menu a {
  display: block;padding: 5px 15px;color:#000;
  text-decoration:none;
}
.top-menu .togg {position: absolute;bottom: -8px;left: 50%;margin-left: -2px;}
.top-menu .togg:before {border-top: 4px solid #f44242;border-left: 4px solid transparent;border-right: 4px solid transparent;content: '';display: block;}
.top-menu .togg.active {transform: rotate(180deg);}
/*Добавим плавный переход для некоторых элементов*/
.togg, a, ul,.top-menu .child {transition: all 0.5s ease 0s;-moz-transition: all 0.5s ease 0s;-webkit-transition: all 0.5s ease 0s;-o-transition: all 0.5s ease 0s;}
@media (min-width: 1200px) {
    /*Делаем горизонтальным. Альтернативный вариант - .parent {display:flex}*/
	.top-menu .parent > li {float: left;}
	/*Показываем подпункты по наведению (В мобильной версии будет по клику)*/
	.top-menu li:hover .child {max-height:1000px;}
	.top-menu ul:after {display:block;content:'';clear:both;}
	.top-menu .child {position: absolute;top: 100%;left: 0px;}
	/*Что бы активную стрелку сделать ярче. Для мобильного не нужно*/
	.top-menu li > .togg:before {opacity:0.5}
	/*Поворот стрелки, отдельно при наведении, что бы не мешало повороту при нажатии*/
	.top-menu li:hover > .togg {transform: rotate(180deg);z-index:10}
    .top-menu li:hover > .togg:before {opacity: 1;}
	/*Выделение цветом при наведении*/
	.top-menu li:hover > a {color: #d60400}
}
@media (max-width: 1199px) {
    /*Покажем кнопку и спрячем меню*/
	.menu-toggle {display:block;}
	.top-menu {
	    display:none;
	    position:absolute;
	    left:0;right:0
	    /*далее ограничу высоту меню и добавлю скрол*/
	    height:80%;overflow:auto;
	}
    .menu-toggle svg {
        padding: 5px;background:rgba(255,255,255,0.5);
        border-radius: 5px;width: 40px;
    }
	/*Зафиксируем страницу при раскрытом меню*/
	body.menuopen {width: 100%;height: 100%;overflow: hidden;}
    /*Подпункты раскрываются по клику на span, что бы было меньше шансов попасть по ссылке, не будем делать ее на всю ширину*/
	.top-menu a {display:inline-block;}
	.top-menu .parent > li {
        border-bottom: 1px dotted #ccc;
        min-height: 36px;
        display: flex;flex-wrap:wrap;
        align-items: center;
    }
    /*Растяну на всю ширину .child, т.к. находится внутри flex*/
    .top-menu .child {width:100%}
    /*Здесь аналогично, как и для desktop*/
	.top-menu .child.open {max-height:1000px}
	.top-menu .togg {
        height: 30px;width: 30px;top: 3px;border: 1px solid #5e5e5e;
        bottom: auto;right: 10px;left: auto;margin-left: 0px;padding: 10px 5px;
        box-sizing: border-box;border-radius: 30px;
    }
	.top-menu .togg:before {
        border-top: 10px solid #ec3437;
        border-left: 9px solid transparent;
        border-right: 9px solid transparent;
    }
}

Результат

Здесь изменил размер окна для перехода к мобильному меню, что бы можно было увидеть мобильное и десктоп в окне codepen.

Есть и некоторые другие отличия, мелкие, в CSS

See the Pen Пример меню by Vladimir (@webrazrab_ru) on CodePen.

Оценок: 1
Надежный хостинг VPS серверов
  • Свои ISO образы
  • VDS с оплатой раз и навсегда
  • Аренда VDS на любой срок, с оплатой по дням
  • Большое разнообразие конфигураций
  • Дата-центры в ЕС и России
+ скидка 10%

Комментарии (0)