it-roy-ru.com

Как реализовать навигатор Dropdown Hover в Bootstrap v4?

Я немного запутался в новой версии начальной загрузки, так как они изменили выпадающее меню на div:

<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <a class="navbar-brand" href="#">Navbar</a>
  <div class="collapse navbar-collapse" id="navbarNavDropdown">
    <ul class="navbar-nav">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Features</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Pricing</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown link
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
    </ul>
  </div>
</nav>

Ребята, у вас есть идея, чтобы получить выпадающий список в ссылке Dropdown в этом фрагменте без добавления дополнительного кода скрипта (только css и скрипт из начальной загрузки)? Я уже видел классы начальной загрузки css и не могу соотнести их с классами начальной загрузки V3 (я делаю это без добавления jquery в V3). 

25
Exprove

Простое решение только для CSS

.dropdown:hover>.dropdown-menu {
  display: block;
}

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


Чтобы обойти это должным образом, нужно использовать события и свойства, зарезервированные для устройств, основанных на указателях: jQuery mouseenter, mouseleave и :hover. Должен работать плавно, интуитивно, не мешая вообще тому, как работает раскрывающийся список на сенсорных устройствах. Попробуйте, дайте мне знать, если это работает для вас:

Полное jQuery решение (touch без изменений):

Решение до версии 4.1.2 (устарело):

$('body').on('mouseenter mouseleave','.dropdown',function(e){
  var _d=$(e.target).closest('.dropdown');
  if (e.type === 'mouseenter')_d.addClass('show');
  setTimeout(function(){
    _d.toggleClass('show', _d.is(':hover'));
    $('[data-toggle="dropdown"]', _d).attr('aria-expanded',_d.is(':hover'));
  },300);
});

$('body').on('mouseenter mouseleave','.dropdown',function(e){
  var _d=$(e.target).closest('.dropdown');
  if (e.type === 'mouseenter')_d.addClass('show');
  setTimeout(function(){
    _d.toggleClass('show', _d.is(':hover'));
    $('[data-toggle="dropdown"]', _d).attr('aria-expanded',_d.is(':hover'));
  },300);
});

/* this is not needed, just prevents page reload when a dd link is clicked */
$('.dropdown a').on('click tap', e => e.preventDefault())
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>

<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <a class="navbar-brand" href>Navbar</a>
  <div class="collapse navbar-collapse" id="navbarNavDropdown">
    <ul class="navbar-nav">
      <li class="nav-item active">
        <a class="nav-link" href>Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href>Features</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href>Pricing</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown link
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
          <a class="dropdown-item" href>Action</a>
          <a class="dropdown-item" href>Another action</a>
          <a class="dropdown-item" href>Something else here</a>
        </div>
      </li>
    </ul>
  </div>
</nav>


v4.1.2 shiplist введено это изменение в том, как работают выпадающие списки, в результате чего вышеуказанное решение больше не работает.
Вот решение в актуальном состоянии, позволяющее открыть раскрывающийся список при наведении курсора в v4.1.2 и выше:

function toggleDropdown (e) {
  const _d = $(e.target).closest('.dropdown'),
    _m = $('.dropdown-menu', _d);
  setTimeout(function(){
    const shouldOpen = e.type !== 'click' && _d.is(':hover');
    _m.toggleClass('show', shouldOpen);
    _d.toggleClass('show', shouldOpen);
    $('[data-toggle="dropdown"]', _d).attr('aria-expanded', shouldOpen);
  }, e.type === 'mouseleave' ? 300 : 0);
}

$('body')
  .on('mouseenter mouseleave','.dropdown',toggleDropdown)
  .on('click', '.dropdown-menu a', toggleDropdown);

function toggleDropdown (e) {
  const _d = $(e.target).closest('.dropdown'),
      _m = $('.dropdown-menu', _d);
  setTimeout(function(){
    const shouldOpen = e.type !== 'click' && _d.is(':hover');
    _m.toggleClass('show', shouldOpen);
    _d.toggleClass('show', shouldOpen);
    $('[data-toggle="dropdown"]', _d).attr('aria-expanded', shouldOpen);
  }, e.type === 'mouseleave' ? 300 : 0);
}

$('body')
  .on('mouseenter mouseleave','.dropdown',toggleDropdown)
  .on('click', '.dropdown-menu a', toggleDropdown);

/* not needed, prevents page reload for SO example on menu link clicked */
$('.dropdown a').on('click tap', e => e.preventDefault())
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>

<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <a class="navbar-brand" href="#">Navbar</a>
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>

  <div class="collapse navbar-collapse" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Link</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <div class="dropdown-divider"></div>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
      <li class="nav-item">
        <a class="nav-link disabled" href="#">Disabled</a>
      </li>
    </ul>
    <form class="form-inline my-2 my-lg-0">
      <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
      <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
    </form>
  </div>
</nav>


Важное примечание: При использовании решения jQuery важно удалить CSS (или раскрывающийся список не закроется при нажатии .dropdown-toggle или при выборе пункта меню).

75
Andrei Gheorghiu

Просто добавьте этот простой CSS код в таблицу стилей, и вы готовы к работе.

.dropdown:hover > .dropdown-menu {
    display: block;
}
.dropdown > .dropdown-toggle:active {
    /*Without this, clicking will make it sticky*/
    pointer-events: none;
}
29
Mohammad Ayoub Khan

У «полного» решения Андрея jQuery + CSS есть правильное намерение, но оно многословно и все еще не полностью. Неполный, потому что, хотя он, вероятно, покрывает все необходимые изменения DOM, он пропускает запуск пользовательских событий . Подробно, потому что заново изобретает колесо, когда Bootstrap уже предоставляет метод dropdown () , который делает все.

Таким образом, правильное решение DRY, которое не зависит от хака CSS, часто повторяемого среди других ответов, - это just jQuery:

$('body').on('mouseover mouseout', '.dropdown', function(e) {
    $(e.target).dropdown('toggle');
});
5
LionelW

Похоже, что функциональность Bootstrap немного изменилась с момента выпуска v4. Элемент .dropdown-menu теперь также получает класс .show в дополнение к .dropdown. Я адаптировал ответ Андрея, чтобы также переключать класс на .dropdown-menu. Обратите внимание, что CSS больше не нужен, и HTML остается тем же, за исключением того, что я обновил ссылки на текущие версии, а класс nav изменился на navbar-expand-md.

$('body').on('mouseenter mouseleave', '.dropdown', function (e) {
    var dropdown = $(e.target).closest('.dropdown');
    var menu = $('.dropdown-menu', dropdown);
    dropdown.addClass('show');
    menu.addClass('show');
    setTimeout(function () {
        dropdown[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show');
        menu[dropdown.is(':hover') ? 'addClass' : 'removeClass']('show');
    }, 300);
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<nav class="navbar navbar-expand-md navbar-light bg-faded">
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
    <span class="navbar-toggler-icon"></span>
  </button>
  <a class="navbar-brand" href="#">Navbar</a>
  <div class="collapse navbar-collapse" id="navbarNavDropdown">
    <ul class="navbar-nav">
      <li class="nav-item active">
        <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Features</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" href="#">Pricing</a>
      </li>
      <li class="nav-item dropdown">
        <a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Dropdown link
        </a>
        <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </li>
    </ul>
  </div>
</nav>

5
Mike Bogochow

Я уже использовал и стилизовал навигационную панель, когда мне было предложено изменить ее на взаимодействие при наведении курсора, поэтому в итоге я исправил это с помощью jQuery.

function bootstrapHoverMenu (bp = 768) {

  // close all dropdowns that are open
    $('body').click( function (e) {
    $('.dropdown-menu.show').removeClass('show');
  });

  // show dropdown for the link clicked
  $('.nav-item').hover(function (e) {
    $('.dropdown-menu.show').removeClass('show');
    if(( $(window).width() >= bp )) {
      $dd = $(this).find('.dropdown-menu');
      $dd.addClass('show');
    }
  });

  // get href for top level link if clicked and open
  $('.dropdown').click(function (e) {
    if( $(window).width() < bp ) {
      $('.dropdown-menu').css({'display': 'none'});
    }
    $href = $(this).find('.nav-link').attr('href');
    window.open($href, '_self');
  });
}

$(document).ready( function() {
   // when page ready run the fix
   bootstrapHoverMenu();
});

Недостатком мобильного является наличие только ссылок верхнего уровня.

2
DerekOBrien

Решение Bootstrap v4 - на основе jQuery, но лучше, чем чистое решение css

Это гарантирует, что вы все еще можете следить за ссылками верхнего уровня и является совместим с мобильным.

Это было сделано с учетом настольных компьютеров и мобильных устройств. Не стесняйтесь обернуть jQuery условием, которое проверяет, превышает ли ширина окна 768 пикселей.

jQuery

/** Dropdown on hover */
$(".nav-link.dropdown-toggle").hover( function () {
    // Open up the dropdown
    $(this).removeAttr('data-toggle'); // remove the data-toggle attribute so we can click and follow link
    $(this).parent().addClass('show'); // add the class show to the li parent
    $(this).next().addClass('show'); // add the class show to the dropdown div sibling
}, function () {
    // on mouseout check to see if hovering over the dropdown or the link still
    var isDropdownHovered = $(this).next().filter(":hover").length; // check the dropdown for hover - returns true of false
    var isThisHovered = $(this).filter(":hover").length;  // check the top level item for hover
    if(isDropdownHovered || isThisHovered) {
        // still hovering over the link or the dropdown
    } else {
        // no longer hovering over either - lets remove the 'show' classes
        $(this).attr('data-toggle', 'dropdown'); // put back the data-toggle attr
        $(this).parent().removeClass('show');
        $(this).next().removeClass('show');
    }
});
// Check the dropdown on hover
$(".dropdown-menu").hover( function () {
}, function() {
    var isDropdownHovered = $(this).prev().filter(":hover").length; // check the dropdown for hover - returns true of false
    var isThisHovered= $(this).filter(":hover").length;  // check the top level item for hover
    if(isDropdownHovered || isThisHovered) {
        // do nothing - hovering over the dropdown of the top level link
    } else {
        // get rid of the classes showing it
        $(this).parent().removeClass('show');
        $(this).removeClass('show');
    }
});

CSS

@media(min-width:  768px) {
  .dropdown-menu {
    margin-top: 0; // fixes closing on slow mouse transition
  }
}
2
Radmation

Я думаю, что это просто работает с bootstrap 4, я добавляю inline, но вы всегда можете связать событие из скрипта.

  <a 
onmouseover="$('#navbarDropdownMenuLink').dropdown('toggle')"
class="nav-link dropdown-toggle" 
href="http://example.com" 
id="navbarDropdownMenuLink" 
data-toggle="dropdown" 
aria-haspopup="true" 
aria-expanded="false">
      Dropdown link
    </a>
1
Fisherman

<div style="width: 100%; overflow: scroll;"><table class="table table-striped table-bordered" style="font-size:12px">

0
Oscar Chamorro

CSS-решения не работают должным образом на сенсорном устройстве

Я обнаружил, что любые CSS-решения заставляют меню оставаться открытым на сенсорных устройствах, они больше не рушатся.

Итак, я прочитал статью: https://www.brianshim.com/webtricks/drop-down-menus-on-ios-and-Android/ (Брайан Шим)
Очень полезно! В нем говорится, что сенсорное устройство всегда сначала проверяет существование класса наведения на элементе.

Но: с помощью jQuery .show () вы вводите атрибут стиля (display: block;), который открывает меню с первого прикосновения. Теперь меню открылось без загрузочного класса show. Если пользователь выбирает ссылку из выпадающего меню, она работает отлично. Но если пользователь решает закрыть меню, не используя его, он должен нажать два раза, чтобы закрыть меню: при первом нажатии исходный класс начальной загрузки «show» присоединяется, поэтому меню открывается снова, при втором нажатии меню закрывается из-за к нормальному поведению начальной загрузки (удаление класса 'show').

Чтобы предотвратить это, я использовал статью: https://codeburst.io/the-only-way-to-detect-touch-with-javascript-7791a3346685 (автор David Gilbertson)

У него есть несколько очень удобных способов обнаружения сенсорных или парящих устройств.

Итак, объединил двух авторов с моим собственным jQuery:

$(window).one('mouseover', function(){
      window.USER_CAN_HOVER = true;
      if(USER_CAN_HOVER){
          jQuery('#navbarNavDropdown ul li.dropdown').on("mouseover", function() {
             var $parent = jQuery(this);
             var $dropdown = $parent.children('ul');

             $dropdown.show(200,function() { 
               $parent.mouseleave(function() {
                 var $this = jQuery(this);
                 $this.children('ul').fadeOut(200);
               });
             });
          });
      };

}); Проверьте один раз, разрешает ли устройство событие наведения мыши. Если это так, представьте возможность зависания, используя .show (). Если устройство не допускает события наведения мыши, функция .show () никогда не вводится, поэтому вы получаете естественное поведение при начальной загрузке на сенсорном устройстве. 

Обязательно удалите все CSS, касающиеся классов при наведении меню.

У меня ушло три дня :), поэтому я надеюсь, что это поможет некоторым из вас.

0
Mike

Это решение включает и выключает

<script>
$(document).ready(function() {
  // close all dropdowns that are open
  $('body').click(function(e) {
      $('.nav-item.show').removeClass('show');
      //$('.nav-item.clicked').removeClass('clicked');
      $('.dropdown-menu.show').removeClass('show');
  });

  $('.nav-item').click( function(e) {
    $(this).addClass('clicked')
  });

  // show dropdown for the link clicked
  $('.nav-item').hover(function(e) {
      if ($('.nav-item.show').length < 1) {
        $('.nav-item.clicked').removeClass('clicked');
      }
      if ($('.nav-item.clicked').length < 1) {
          $('.nav-item.show').removeClass('show');
          $('.dropdown-menu.show').removeClass('show');
          $dd = $(this).find('.dropdown-menu');
          $dd.parent().addClass('show');
          $dd.addClass('show');
      }
  });
});</script>

Чтобы отключить наведение для меню свертки размера lg, добавьте

if(( $(window).width() >= 992 )) {
0
Tom Hansen

Bootstrap 4 только для CSS

Ни один из ответов CSS не работает полностью. Либо раскрывающееся меню остается открытым после щелчка, либо имеется пробел, который скрывает раскрывающееся меню, прежде чем вы сможете перейти по ссылкам меню, чтобы щелкнуть.

Вот простое решение только для CSS:

.navbar-nav li:hover .dropdown-menu {
    display: block;
}

Удалите data-toggle=dropdown из разметки HTML, чтобы раскрывающийся список оставался открытым при щелчке. Используйте mt-0 (margin-top: 0), чтобы устранить пробел над меню и сделать возможным перемещение элементов меню.

Демо https://www.codeply.com/go/awyU7VTIJf


Полный код:

   .navbar-nav li:hover .dropdown-menu {
        display: block;
    } 

   <nav class="navbar navbar-expand-lg navbar-light bg-light">
      ..
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
          <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown">
              Dropdown
            </a>
            <div class="dropdown-menu mt-0" aria-labelledby="navbarDropdown">
              <a class="dropdown-item" href="#">Action</a>
              <a class="dropdown-item" href="#">Another action</a>
              <div class="dropdown-divider"></div>
              <a class="dropdown-item" href="#">Something else here</a>
            </div>
          </li>
        </ul>
      </div>
    </nav>   
0
Zim