Открыть меню    

Выпадающий блок из блока с overflow hidden

выпадающий блок из блока с overflow hiddenИтак, есть задача создать прокручиваемое меню (с фиксированной высотой и шириной) с подменю, причем подменю должно появляться, когда мышка наведена на родительский пункт меню.

Или, другими словами, наша задача сделать так, чтобы блок выпадающий из блока с 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

Комментарии к статье

Добавить комментарий