Выпадающее меню на HTML и CSS

меню

Выпадающее меню можно сделать на CSS не используя скрипты и модули. Просто дописываем несколько строк в CSS файл и готово. Несмотря на простоту меню будет достаточно универсальным. Стили подходить под произвольное количество уровней вложенности. Хоть два хоть десять уровней. Код при этом остаётся тем же. В статье описаны три вида меню, но если вы только начинаете разбираться в вёрстке советую читать сначала. Так будет проще понять.

  1. Вертикальное выпадающее меню
  2. Горизонтальное выпадающее меню с одним уровнем вложенности
  3. Горизонтальное меню с несколькими уровнями вложенности

Сначала сделаем HTML разметку для меню. Принято делать меню списком. Это не стандарт просто так сложилось.

<ul class="nav">
  <li><a href="#">пункт 1</a>
    <ul>
      <li><a href="#">пункт 1.1</a></li>
      <li><a href="#">пункт 1.2</a></li>
    </ul>
  </li>
  <li><a href="#">пункт 2</a>
    <ul>
      <li><a href="#">пункт 2.1 многа букаф</a></li>
      <li><a href="#">пункт 2.2</a>
        <ul>
          <li><a href="#">пункт 2.2.1</a></li>
          <li><a href="#">пункт 2.2.2</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#">пункт 3</a></li>
  <li><a href="#">пункт 4</a></li>
  <li><a href="#">пункт 5</a></li>
</ul>

HTML файл мы больше трогать не будем. Все дальнейшие изменения производятся только за счёт CSS. Первое что нам нужно сделать - скрыть вложенные пункты.

ul.nav li ul {display: none;}

Ещё нужно убрать маркеры но это дело вкуса.

ul.nav li {list-style: none;}

Чтобы вложенные пункты появлялись при наведении на родительский пункт используем псевдокласс :hover, при этом нужно сделать так что бы появлялись только пункты первого уровня вложенности. Для этого используем знак >

ul.nav li:hover > ul {display: block;} 

В этой строчке заключается вся магия нашего выпадающего меню, поэтому разберём её подробнее. Благодаря псевдоклассу :hover, при наведении курсора на пункт меню, он же элемент списка li, для первого вложенного списка ul значение display станет block. Так открывается первый уровень вложенности. Если на этом уровне есть пункты содержащие подпункты, то при наведении на них также откроется только один вложенный уровень.

С основной механикой разобрались. Осталось переопределить место где будут появляться вложенные пункты. Для этого воспользуемся свойством position: absolute. При таком позиционировании отсчёт координат ведётся от края окна браузера если только для родительского элемента не задано свойство position с значением fixed, relative или absolute. В этом случае отсчёт ведётся от края родительского элемента.

Вертикальное выпадающее меню

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

/* Вертикальное выпадающее меню*/
body{
  background: #DCDCDC;
}
/*блок меню*/
ul{
  margin: 0;
  padding: 0;
}
ul.nav li {
  background: #B3B3FF;
  border: 1px solid #FFFFFF;
  list-style: none;
  width: 150px;         /*ширина блока меню*/
}
ul.nav li a {
  text-decoration: none;
  display: block;
  padding: 5px 5px 5px 15px;
}
ul.nav li ul {
  display: none;	/*скрываем вложенные пункты*/
}

/*Выпадающее меню*/

ul.nav li:hover {
  /* позиционирование вложенных элементов
   * будет расчитыватьться относительно
   * родительского элемента
   */
  position: relative;
  background: yellow;
}
ul.nav li:hover > ul {
  display: block;
}
ul.nav li:hover ul{
  position: absolute;
  top: 0;       /*Задаём координаты для вложенных пунктов*/
  left: 150px; /*меню раскрывается вправо*/
}

Вот что должно получиться в результате. демка

вертикальное выпадающее меню

Горизонтальное выпадающее меню

Для горизонтального меню мы будем так же применять позиционирование но есть несколько нюансов. Что бы сделать меню горизонтальным используем свойство float. Меню будет раскрываться вниз значит при позиционировании нужно учитывать высоту. Для начала сделаем меню с одним уровнем вложенности. Вложенное меню также будет горизонтальным. Для этого вложенное меню позиционируем относительно списка а не родительского пункта и задаём для него ширину.

/*Горизонтальное выпадающее меню с одним уровнем вложенности*/

body{
  background: #DCDCDC;
}

/* Блок меню*/

ul{
  margin: 0;
  padding: 0;
}
ul.nav li {
  background: #B3B3FF;
  border-right: 1px solid #FFFFFF;
  float: left; /*делаем меню горизонтальным*/
  height: 30px;
  list-style: none;
}
ul.nav li a {
  text-decoration: none;
  display: block;
  padding: 5px 5px 5px 15px;
}
ul.nav li ul {
  display: none;	/*скрываем вложенные пункты*/
}
ul.nav li:hover {
  background: yellow;
}
/*Выпадающее меню*/
ul.nav {
  position: relative;
  background: #B3B3FF;
  height: 30px;
  width: 600px;
}
ul.nav li:hover > ul {
  background: #D0E0FF;
  border-top: 1px solid white;
  display: block;
  width: 600px;
  position: absolute;
  top: 30px;
  left: 0;
}

Вот что получиться в итоге демка.

горизонтальное выпадающее меню

Многоуровневое горизонтальное выпадающее меню

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

/*Горизонтальное выпадающее меню a*/

body{
  background: #DCDCDC;
}

/* Блок меню*/
ul{
  margin: 0;
  padding: 0;
}
ul.nav li {
  background: #B3B3FF;
  border-right: 1px solid #FFFFFF;
  float: left; /*делаем меню горизонтальным*/
  height: 30px;
  list-style: none;
}
ul.nav li a {
  text-decoration: none;
  display: block;
  padding: 5px 5px 5px 15px;
}
ul.nav li ul {
  display: none;	/*скрываем вложенные пункты*/
}
ul.nav { /*задаём высоту и ширину меню*/
  background: #B3B3FF;
  height: 30px;
  width: 600px;
}

/*Выпадающее меню*/

ul.nav li:hover {
  background: yellow;
  position: relative;
}
ul.nav li:hover > ul {
  border-top: 1px solid white;
  display: block;
  position: absolute;
  top: 30px; /*первый уровень меню раскрывается вниз*/
  left: 0;
}
/*Второй и последующие уровни вложенности*/
ul.nav li ul li{
  border-bottom: 1px solid white;
  height: auto;
  width: 150px;
}
ul.nav li:hover ul li ul{
  position: absolute;
  top: 0;
  left: 150px; /*второй и последующие уровни расскрываются вправо*/
}

Вот так будет выглядеть наше многоуровневое меню демка.

Горизонтальное многоуровневое меню

Добавлено пользователемЮрийon ср, 04/02/2014 - 16:35

Спасибо огромное! 
Задолбался я иcкать решение простое до нельзя, что бы было только нужное и больше ничего лишнего. 
А то раздуют css непонятно чем. Здесь же все просто и локанично... 

СпасибО! 

Интересно, а для Вордпресс такое решение не подходит? Довольно просто все изложено, сделать сможет любой. Отличные советы, без лишней "шелухи"!

Добавлено пользователемЮлияon вс, 05/04/2014 - 08:57

Очень полезная информация. Когда такое меню хотела реализовать у себя на блоге, то пришлось обращаться к веб-мастеру. А теперь могу сесть и потихоньку реализовать такую задумку сама. Подписалась на Ваш блог. Буду изучать.

Что-то мой комментарий не виден - дублирую:

===========
Спасибо, но у меня (под Инт.Экспл-8) меню не выпадает. Т.е. не работает строка "ul.nav li:hover ul {display: block;}"
Подскажите, в чем дело?

Есть пожедания по изложению материала:

-хорошо бы в этой строке (ul.aaaaa li:hover ul {display: block;}) растолковать, какая ее часть отвечает за "выпадение меню", а какая - за скрывание пунктов более глубокого уровня вложенности

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

-в частности, неясно, зачем нужна строка "<div class="block-menu">

-перед этой строкой стоят 2 строки, видимо, не относящиеся к делу (<a href="/html">Домой</a><br /> и <a href="/html/drop_down_menu">Посмотреть вертикальное меню</a>). У человека, незнакомого с новым материалом (то есть, как раз для того, для которого вы писали), уходит время на осознание того, что эти строки для выпадающего меню не нужны. Или все-таки нужны?

-в хеддере в вашем примере имеются 3 строки. Из них только одна относится к теме (задает имя ЦСС-файла), а 2 другие - нет. Это также осложняет освоение материала. Тем более, что на моем компьютере такой хеддер вообще приводит к некорректному отображению текста, поскольку мне нужно ставить не  "utf-8", а "Windows-..."

-в примере неясно, что слова "style.css", "block-menu" и "nav" - это не ключевые слова, а названия, задающиеся пользователем. Я выяснял это экспериментально

Короче говоря, хотелось бы, чтобы в примерах было только то, что имеет отношение к делу, причем, чтобы про неясные по контексту строки было бы написано, какое именно "отношение к делу" они имеют.

Самый объёмный коммент!!!
Коментарии проходят только после модерации поэтому вы и не увидели свой коммент сразу.
Постараюсь ответить на всё. Начнём с вопросов зачем здесь это. Таки да две ссылки просочились с рабочего варианта.
Насчёт не корректного отображения текста. Вы смогли прочитать и откоментить эту страницу при том что кодировка у неё такая же как и у примера в посте.
Данный пост не предназначен для абсолютных новичков и был написан по желанию подписчиков. Если объяснять абсолютно всё то нужно будет рассказать про классы и идентификаторы, блоки, позиционирование, псевдоселекторы, каскад и прочее. Довольно большая статья получиться и что самое главное бесполезная.
ul.nav li:hover > ul {display: block;} будет показывать ранее скрытый пункт. Если разбирать по буквам то(здесь параллельно смотрим на код ) при наведении на пункт меню(ul.nav li) сработает :hover и первый вложенный список >ul будет виден за счёт того что значение display сменит значение с none на block.

Поправил статью с учётом ваших замечаний. Про кодировку честно говоря забыл что виндовс сохраняеи файлы в windows-1251 Тут два варианта или как вы заметили указать форточную кодировку в файле или изменить кдировку файла например в Notepad++.

Спасибо за критику приходите к нам ещё

Добавлено пользователемЕвгенийon вс, 05/04/2014 - 16:55

Меню некоректно отображается в safari IE Mozila Firefox не подскажите в чем может быть проблема?

Добавлено пользователемАлексийon вс, 10/12/2014 - 01:45

Все понятно, но если сделать меню справа в блоке, а выпадающие пункты влево (далее код) :

<html>
<head>
<style>

ul, li {
    margin:0;
    padding:0;
    list-style-type:none; 
}
 

#menu {
    display:block;
    float: right;
        
}
 

#menu > li {
    display:inline-block;
    position:relative;
}
 

#menu > li > ul {
    display:none; 
}
#menu > li > ul > li > ul {
    display:none;
}
 

#menu > li:hover > ul {
    display:block;
    float:left;
}
#menu > li > ul > li:hover > ul {
    display:block;
    float:left;
}

</style>
</head>
<body>
<ul id="menu">
    <li><a href="#">МЕНЮ</a>
        <ul>
            <li><a href="#">Раздел1</a></li>
            <li><a href="#">Раздел2</a></li>
            <li><a href="#">Раздел3</a>
             <ul>
            <li><a href="#">Подраздел1</a></li>
            <li><a href="#">Подраздел2</a></li>
            <li><a href="#">Подраздел3</a></li>
            <li><a href="#">Подраздел4</a></li>
            <li><a href="#">Подраздел5</a></li>
            <li><a href="#">Подраздел6</a></li>
            <li><a href="#">Подраздел7</a></li>
        </ul>
        </li>
        </ul>
    </li>
  </ul>
</body>
</html>

, то при наведении на раздел 3 выпадает список с подразделами, однако пункты раздел 1 и раздел 2 тоже уезжают влево на уровень подразделов. Как не бился не могу исправить...

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

Если открывать меню влево применяя float то нужно быть готовым к тому что вся вёрстка сдвинется влево. Это не произойдёт если делать выпадающее меню через позиционирование как описано в статье.

 

Добавлено пользователемserg_rcon пн, 01/12/2015 - 14:40

Уважаемый OLEG, подскажите - можно-ли сделать так, чтобы после выбора какого-то пункта подменю все "выпавшие" подменю сворачивались и оставался только самый верхний уровень ?

Описание в статье меню сворачиваются при клике или если убрать курсор. Если Вы имеете ввиду менюшки которые разворачиваются по клику то здесь нужен Javascript и это отдельная тема о которой расскажу в одной из ближайших статей.

Если же вас интересует чистый HTML/CSS то можно попробовать через инпуты но это больше похоже на шаманство и на "чистый" ни как не тянет.

Да в том-то и дело, что сворачивается меню, описанное в статье (по крайней мере то, которое в демке) только если убрать курсор. А в том случае, когда кликаешь по какому-то пункту, но курсор не сдвигаешь - с меню ничего не происходит, оно так и остается развернутым. В своем проекте я частично решил проблему - меняю css-свойство visibility: visible/hidden туда-сюда. Частично потому, что нормально это работает только в браузерах на WebKit. В Mozilla и IE меню исчезает и снова появляется, если не сдвигать курсор с места. Я просмотрел много вариантов меню - и пока не нашел ниодного варианта, устроившего-бы меня - чаще всего после выбора пункта меню происходит переход на другую страницу и толгда менб естественно пропадает. А если перехода на новую страницу нет после выбора пункта меню так и остается, пока не сдвинешь курсор за его пределы..

На мой взгляд единственный выход это Javascript.  Например повесить на клик добавление к пункту меню ID для которого в стилях прописать visibility: hidden. Чтобы вернуть меню в исходное состояние удаляем добавленный ID после того как пользователь уберёт курсор и произойдёт событие mouseout

Добавлено пользователемPuncheron сб, 10/22/2016 - 21:41

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

Добавлено пользователемСергейon чт, 11/17/2016 - 12:28

Здравствуйте! Спасибо большое за статью! У меня тоже возник такой вопрос: В случае когда много уровней то последние списки вылазят за пределы окна. Как сделать чтобы при достижении правой границы окна, следующий уровень перешол как бы на новую строку.
Вот скриншот: http://joxi.net/5mdz8aMClznWA1.
И ещё как сделать чтобы списки были одинковой высоты. Если высота какого-то списка будет больше чем у остольных, то чтобы и остальные списки применили такую высоту. У li:hover я задал и свойство z-index: 2 чтобы отображались поверх второго блока. Спасибо! Жду Вашего ответа!

На вашем месте я бы не стал переносить пункт меню. Для пользователя не очевидно что новый подпункт открывается в другом месте. Ещё один момент. Для клика на этот пункт ему придётся перевести курсор, hover перестанет работать меню свернётся. Я бы на вашем месте сделал первый уровень открывающимся вниз, между основными пунктами меню.

Чтобы списки подстраивались по высоте... Даже не знаю как такое сдеать на css

Добавлено пользователемОлесяon ср, 12/11/2019 - 07:07

Спасибо