Выпадающий блок из блока с overflow hidden
Итак, есть задача создать прокручиваемое меню (с фиксированной высотой и шириной) с подменю, причем подменю должно появляться,
когда мышка наведена на родительский пункт меню.
Или, другими словами, наша задача сделать так, чтобы блок выпадающий из блока с overflow hidden
был виден.
Просто!
Создайте список для меню, добавьте некоторые вложенные списки для подменю, расположите вложенные списки относительно пунктов родительского меню, вуаля!
HTML
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
<li class="parent">Abc >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Def</li>
<!-- .... -->
<li>Xyz</li>
</ul>
CSS
ul { width: 200px;max-height: 250px;overflow: auto;}
li { position: relative;}
li ul {position: absolute; top: 0;left: 75%; z-index: 10;display: none;}
li:hover > ul { display: block;}
ul {margin: 1em; color: white; font-family: sans-serif; font-size: 16px;}
li { padding: 1em;}
li ul {margin: 0;}
li ul {cursor: auto;}
li ul li {padding: 0.5em;}
li:nth-child(2n) {background: #0e8ce0;}
li:nth-child(2n+1) {background: #0064b3;}
li.parent { background: #00b99b; cursor: pointer;}
Подождите, но это не правильно. Да, конечно же, мы используем overflow: auto
, - возможно,
если мы будем использовать overflow-x: visible
, то блоки, выходящие за пределы элемента по горизонтали, станут видимыми.
HTML
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
<li class="parent">Abc >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Def</li>
<!-- .... -->
<li>Xyz</li>
</ul>
CSS
ul{width: 200px;max-height: 250px;overflow-x: visible; overflow-y: scroll;}
li {position: relative;}
li ul {position: absolute;top: 0;left: 75%;z-index: 10;display: none;}
li:hover > ul {display: block;}
ul { margin: 1em; color: white; font-family: sans-serif; font-size: 16px;}
li { padding: 1em;}
li ul {margin: 0;}
li ul { cursor: auto;}
li ul li { padding: 0.5em;}
li:nth-child(2n) {background: #0e8ce0;}
li:nth-child(2n+1) { background: #0064b3;}
li.parent { background: #00b99b; cursor: pointer;}
Что происходит? Почему по-прежнему присутствует скроллбар?
Проблема
Рассмотрим, что говорит спецификация W3C:
Вычисленные значения для overflow-x
и overflow-y
равнозначны заданным значениям, за исключением следующего: некоторые комбинации со значением visible
невозможны, например, если для одного из свойств (overflow-x
и overflow-y
) определено значение visible
, а для другого scroll
или auto
, то visible
устанавливается в auto
.
Например, это:
CSS
overflow-x: visible;
overflow-y: auto;
Превращается в это:
CSS
overflow-x: auto;
overflow-y: auto;
Поэтому мы не можем видеть выходящие по горизонтали блоки, если по вертикали блоки невидимы (overflow-y
равен scroll
или auto
) и наоборот.
Решение
Любопытно, если пренебречь объявлением position: relative
для пункта меню, то подменю будет видно, и расположится относительно ближайшего позиционируемого предка (в нашем примере это элемент с классом .box-new
).
HTML
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
<li class="parent">Abc >
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</li>
<li>Def</li>
<!-- .... -->
<li>Xyz</li>
</ul>
CSS
ul{width: 200px;max-height: 250px;overflow: auto;}
li ul {position: absolute; top: 0;left: 75%;z-index: 10;display: none;}
li:hover > ul { display: block;}
ul { margin: 1em; color: white; font-family: sans-serif; font-size: 16px;}
li { padding: 1em;}
li ul { margin: 0;}
li ul { cursor: auto;}
li ul li { padding: 0.5em;}
li:nth-child(2n) {background: #0e8ce0;}
li:nth-child(2n+1) { background: #0064b3;}
li.parent { background: #00b99b; cursor: pointer;}
В принципе, для того чтобы абсолютно позиционируемый элемент показывался за элементом с overflow: hidden
, его ближайший позиционируемый предок также должен быть предком элемента с overflow: hidden
.
Зная это, мы можем добавить оболочку вокруг меню с целью играть роль ближайшего позиционируемого предка для каждого подменю. Затем, когда пользователь наводит мышку на пункт меню, мы можем позиционировать оболочку подменю, используя немного javascript.
HTML
<div class="wrapper">
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<div class="wrapper">
<ul>
<li>Abc</li>
<!-- .... -->
<li>Jkl</li>
<li class="parent">Mno >
<div class="wrapper">
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</div>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</div>
</li>
<li>Pqr</li>
<!-- .... -->
<li>Xyz</li>
<li class="parent">Abc >
<div class="wrapper">
<ul>
<li>Abc</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</div>
</li>
<li>Def</li>
<!-- .... -->
<li>Xyz</li>
</ul>
</div>
CSS
.wrapper{ position: relative;}
ul { width: 200px; max-height: 250px; overflow-x: hidden;overflow-y: auto;}
li { position: static;}
li .wrapper { position: absolute; z-index: 10; display: none;}
li:hover > .wrapper { display: block;}
ul { margin: 1em; color: white; font-family: sans-serif; font-size: 16px;}
li { padding: 1em;}
li ul { margin: 0;}
li .wrapper { cursor: auto;}
li .wrapper li { padding: 0.5em;}
li:nth-child(2n) { background: #0e8ce0;}
li:nth-child(2n+1) {background: #0064b3;}
li.parent { background: #00b99b; cursor: pointer;}
jQuery
$(function() {
// всякий раз, когда мы наведем курсор мышки на пункт меню, кот. имеет подменю
$('li.parent').on('mouseover', function() {
var $menuItem = $(this),
$submenuWrapper = $('> .wrapper', $menuItem);
// определяем координаты относительно ближайшего эл-та родителя,
// у кот. задан тип позиционирования
var menuItemPos = $menuItem.position();
// располагаем подменю в корректной позиции относительно пункта меню
$submenuWrapper.css({
top: menuItemPos.top,
left: menuItemPos.left + Math.round($menuItem.outerWidth() * 0.75)
});
});
});
Поскольку теперь ни пункты меню, ни само меню не позиционированы, то подменю способны выйти за пределы блоков с overflow: hidden
. Теперь мы можем иметь столько вложенных меню, сколько нам необходимо.
Вот так. По матриалам статьи: Popping Out of Hidden Overflow
Комментарии к статье
Увы не мой случай(