Выделяем в меню навигации элементы, содержащие подменю более низких уровней
Вам должно быть известно, что в JQuery есть метод, который позволяет выбрать элемент меню навигации, даже если он принадлежит параллельной, неактивной ветке иерархии. К сожалению, в CSS на текущий момент еще не прописаны инструменты, задания параметров такого объекта. Но если вы знаете кое-что о структуре HTML, вы можете использовать комбинацию селекторов, чтобы эмулировать его.
В последнее время я работал над элементами выпадающего меню и хотел, чтобы можно было различать те пункты главного меню, которые содержат дочерние подменю, от тех, в которых нет более низких уровней. Я хотел, чтобы делалось это автоматически, не полагаясь на средства JavaScript, и без необходимости создавать новые классы в коде страницы. Я просто хотел, чтобы можно было сразу определять, какие пункты навигации имеют вложенные подменю, и в каких их нет.
Необходимость в подсказках для меню навигации
Как вы, наверное, знаете, структурно меню навигации – это списки ссылок, а посему, как правило, их размечают с помощью тэгов списка <ul>. В более широком смысле, выпадающие меню – это просто вложенные списки <ul> в другой список, более высокого порядка. Элементы меню типа Dropdown являются обычным компонентом для современных (и не очень) веб-разработок.
Использование чистого CSS позволяет разместить в любом месте страницы элементы меню верхнего уровня. При этом остальные пункты, более низкого уровня, остаются скрытыми, и путь к ним разворачивается только при наведении мыши (или нажатии) на соответствующие области.
Многим дизайнерам этого достаточно, и они оставляют ситуацию, как есть. Однако с точки зрения UX / UI это не совсем то, что нужно. Пользователям придется изучить все пункты меню, чтобы понять, какие разделы содержат дополнительные подменю. То есть они будут затрачивать на поиск лишнее время. Что не нравится никому. Или же вовсе не смогут найти какие-нибудь разделы вашего сайта! А это уже совсем нездорово.
Распространенный способ решения данной проблемы – просто добавить отдельный класс для списков <li>, которые содержат вложенные списки <ul> (подменю). Их стиль будет отличаться от тех элементов, которые не содержат пунктов более низкого уровня. Довольно простой, но в то же время и довольно утомительный способ. К тому же не особенно изящный.
Еще один метод, при условии, что элементы меню верхнего уровня не содержат специальных кодов для ссылок – использовать непосредственно для разных элементов разную разметку с помощью тэгов. То есть, задать через тэг специальный стиль для структурированных пунктов меню.
Он будет показывать, что этот элемент имеет более низкие уровни. И, наоборот, для элементов меню, которые не имеют подпунктов, задается обычный стиль анкора ссылки. Данный способ удобен, если выпадающие элементы вашего меню навигации довольно просты, и вы можете структурировать их таким образом.
Это проще, чем создавать отдельные стили, но вам все еще нужно править код вручную. Или же ваша CMS должна сама определять, какой список будет содержать элементы более низких уровней.
Делаем все автоматически!
Я просматривал и другие методы для автоматического назначения стилей спискам, которые содержат вложенные уровни, но в них использовалось абсолютное позиционирование и псевдо элементы для дочерних списков. А более интеллектуальные, с использованием расчетов CSS, могут быть в разы сложнее.
Или же метод может быть совершенно неприменим вследствие несовместимости с другими технологиями, примененными на сайте и/или невозможности использовать вместе с ним по визуальные эффекты, выбранные веб-дизайне для вывода выпадающих меню.
Оптимальный способ
В идеале было бы замечательно иметь возможность переключать стили CSS, в зависимости от результатов запроса: имеет ли данный пункт навигации дочерние элементы или нет. Что-то наподобие метода JQuery .has().
Мы можем достичь приблизительно того же результата с помощью селектора-функции li a:first-child:nth-last-child(x) { }.
Ключ - в отслеживании количества дочерних элементов (HTML-элемент планирования подчиненных пунктов меню?). Как правило, могут иметь место два типа элементов: анкор – пункт меню, не имеющий низших уровней и список <ul>, состоящий из подчиненных элементов.
Хотя вы можете настроить систему так, чтобы отслеживалось любое количество типов объектов или дочерних элементов. Настолько, насколько разветвленные меню имеются у вас на сайте.
Вот небольшой пример. Разметка здесь, как для обычного вложенного списка. И обратите внимание: я использовал только один класс в HTML – для корневого списка. Можете сами убедиться в этом, добавляя любое количество вложенных списков на любом уровне.

Создание выпадающего демо-меню
Давайте протестируем данную модель!
HTML-код: скопируйте его в точности:
<nav>
<ul class="nav">
<li><a href="#">About</a></li>
<li><a href="#">Portfolio</a>
<ul>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
</ul>
</li>
<li><a href="#">Resume</a>
<ul>
<li><a href="#">item a lonng submenu</a></li>
<li><a href="#">item</a>
<ul>
<li><a href="#">Ray</a></li>
<li><a href="#">Veronica</a></li>
<li><a href="#">Bushy</a></li>
<li><a href="#">Havoc</a></li>
</ul>
</li>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
</ul>
</li>
<li><a href="#">Download</a></li>
<li><a href="#">Rants</a>
<ul>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
<li><a href="#">item</a></li>
</ul>
</li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
CSS
Несколько стандартных стилей, чтобы сделать меню немного привлекательнее.
nav {
display: block;
text-align: center;
}
nav ul {
margin: 0;
padding:0;
list-style: none;
}
.nav a {
display:block;
background: #111;
color: #fff;
text-decoration: none;
padding: 0.8em 1.8em;
text-transform: uppercase;
font-size: 80%;
letter-spacing: 2px;
text-shadow: 0 -1px 0 #000;
position: relative;
}
.nav{
vertical-align: top;
display: inline-block;
box-shadow:
1px -1px -1px 1px #000,
-1px 1px -1px 1px #fff,
0 0 6px 3px #fff;
border-radius:6px;
}
.nav li {
position: relative;
}
.nav > li {
float: left;
border-bottom: 4px #aaa solid;
margin-right: 1px;
}
.nav > li > a {
margin-bottom: 1px;
box-shadow: inset 0 2em .33em -0.5em #555;
}
.nav > li:hover,
.nav > li:hover > a {
border-bottom-color: orange;
}
.nav li:hover > a {
color:orange;
}
.nav > li:first-child {
border-radius: 4px 0 0 4px;
}
.nav > li:first-child > a {
border-radius: 4px 0 0 0;
}
.nav > li:last-child {
border-radius: 0 0 4px 0;
margin-right: 0;
}
.nav > li:last-child > a {
border-radius: 0 4px 0 0;
}
.nav li li a {
margin-top: 1px;
}
И вот – волшебство:
.nav li a:first-child:nth-last-child(2):before {
content: "";
position: absolute;
height: 0;
width: 0;
border: 5px solid transparent;
top: 50% ;
right:5px;
}
По сути это и есть основной активный элемент данной техники. В этом примере я использовал: перед закрытием псевдо элемента разместил привязку к элементу, который выводит стрелку.
Хотя для этой техники использование псевдо элемента и не обязательно. Я мог бы так же легко изменить фон анкора. Вы можете и сами выделить целевой элемент так, как вам нравится.
И для завершения несколько кодов для размещения элементов и редактирования стиля стрелок.
CSS
/* размещение подменю*/
.nav ul {
position: absolute;
white-space: nowrap;
border-bottom: 5px solid orange;
z-index: 1;
left: -99999em;
}
.nav > li:hover > ul {
left: auto;
margin-top: 5px;
min-width: 100%;
}
.nav > li li:hover > ul {
left: 100%;
margin-left: 1px;
top: -1px;
}
/* стиль стрелок */
.nav > li > a:first-child:nth-last-child(2):before {
border-top-color: #aaa;
}
.nav > li:hover > a:first-child:nth-last-child(2):before {
border: 5px solid transparent;
border-bottom-color: orange;
margin-top:-5px
}
.nav li li > a:first-child:nth-last-child(2):before {
border-left-color: #aaa;
margin-top: -5px
}
.nav li li:hover > a:first-child:nth-last-child(2):before {
border: 5px solid transparent;
border-right-color: orange;
right: 10px;
}
Как вы, наверное, догадались, вы можете использовать и другие селекторы/комбинации селекторов. Например, :only-child, :first-child:last-child, :first-child:not(:last-child) или что вам больше нравится. Но я обнаружил, что комбинация :nth-child(x):nth-last-child(x)обеспечивает наибольшую гибкость настройки и служит в качестве встроенной функции, позволяющей нам определять нужный элемент напрямую, а не путем исключения. А также имеет перед другими селекторами преимущество в виде большего количества браузеров, поддерживающих ее.
Вот и все. Просто, изящно и полностью автоматизировано. Как и должно быть. Данная техника применима практически для всех браузеров. За исключением IE, который, как всегда, немного пробуксовывает и имеет поддержку данной функции только в версии 9 и выше.
На момент написания данной статьи, ориентировочное число браузеров, поддерживающих эти селекторы, составляет порядка 87%. Что, согласитесь, не так уже и плохо.