web-dev-qa-db-fra.com

Comment implémenter un survol Navbar Dropdown dans Bootstrap v4?

Je suis un peu confus sur la nouvelle version de bootstrap car ils ont changé les menus déroulants en divs:

<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>

Avez-vous la moindre idée d’obtenir une liste déroulante en survol dans le lien Dropdown de cet extrait sans ajouter de code de script supplémentaire (uniquement css et script à partir de bootstrap)? J'ai déjà vu les classes css de bootstrap et je ne peux pas relier celles de bootstrap V3 (j'y parviens sans ajouter jquery dans V3). 

25
Exprove

Simple, solution CSS uniquement

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

Lorsque vous cliquez dessus, la classe show sera toujours basculée (et restera ouverte lorsqu'elle ne sera plus survolée).


Pour contourner ce problème, correctement consiste à utiliser des événements et des propriétés réservées aux périphériques basés sur un pointeur: jQuery's mouseenter, mouseleave et :hover. Devrait fonctionner sans heurts, intuitivement, sans interférer du tout avec le fonctionnement de la liste déroulante sur les appareils tactiles. Essayez-le, laissez-moi savoir si cela fonctionne pour vous:

Solution complète jQuery (touch intacte):

Solution antérieure à la version 4.1.2 (obsolète):

$('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 introduit ce changement comment fonctionnent les menus déroulants, rendant la solution ci-dessus inutilisable.
Voici la solution mise à jour pour ouvrir le menu déroulant au survol dans v4.1.2 et versions ultérieures:

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>


Remarque importante: Si vous utilisez la solution jQuery, il est important de supprimer le CSS (sinon la liste déroulante ne se fermera pas lorsque vous aurez cliqué sur .dropdown-toggle ou sur une option de menu).

75
Andrei Gheorghiu

Ajoutez simplement ce code css simple dans votre feuille de style et vous êtes prêt à partir.

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

La solution "complète" jQuery + CSS d’Andrei a la bonne intention, mais elle est détaillée et toujours incomplète. Incomplet parce que, même s'il couvre probablement tous les changements nécessaires du DOM, il manque le déclenchement de événements personnalisés . Verbose car il réinvente la roue lorsque Bootstrap fournit déjà la méthode dropdown () , qui fait tout.

Donc, la solution correcte, DRY, qui ne repose pas sur le hack CSS souvent répété entre autres réponses, est just jQuery:

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

La fonctionnalité de Bootstrap semble avoir légèrement changé depuis la publication de la v4. L’élément .dropdown-menu semble désormais recevoir également la classe .show en plus du .dropdown. J'ai adapté la réponse d'Andrei pour basculer également la classe sur le .dropdown-menu. Notez que le CSS n'est plus nécessaire et que le code HTML est le même, sauf que j'ai mis à jour les liens vers les versions actuelles et que la classe de navigation a été remplacée par 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

J'avais déjà utilisé et mis en forme une barre de navigation lorsqu'il m'avait été demandé de la remplacer par une interaction en survol.

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();
});

L'inconvénient est que le mobile n'a que des liens de premier niveau.

2
DerekOBrien

Solution Bootstrap v4 - basée sur jQuery, mais meilleure qu'une solution de css pure

Cela garantit que vous pouvez toujours suivre les clics de lien de niveau supérieur et qu'il est compatible avec le mobile.

Cela a été construit avec ordinateur de bureau et mobile à l'esprit. N'hésitez pas à envelopper le jQuery avec une condition qui vérifie si la largeur de la fenêtre est supérieure à 768px.

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

Je pense que cela fonctionne simplement avec bootstrap 4, j’ajoute in-line mais vous pouvez toujours lier un événement à partir d’un script.

  <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

Les solutions CSS ne fonctionnent pas correctement sur un périphérique tactile

J'ai constaté que toutes les solutions CSS faisaient en sorte que le menu reste ouvert sur les appareils tactiles, elles ne s'effondraient plus.

J'ai lu l'article: https://www.brianshim.com/webtricks/drop-down-menus-on-ios-and-Android/ (par Brian Shim)
Très utile! Il indique qu'un périphérique tactile vérifie toujours d'abord l'existence d'une classe de survol sur un élément.

Mais: en utilisant jQuery .show (), vous introduisez un attribut de style (display: block;) qui rend le menu ouvert au premier toucher. Maintenant, le menu a été ouvert sans la classe bootstrap 'show'. Si un utilisateur choisit un lien dans le menu déroulant, cela fonctionne parfaitement. Mais si un utilisateur décide de fermer le menu sans l'utiliser, il doit taper deux fois pour fermer le menu: au premier toucher, la classe d'amorçage "show" d'origine est attachée pour que le menu s'ouvre à nouveau, au second toucher, le menu se ferme comportement d'amorçage normal (suppression de la classe 'show').

Pour éviter cela, j'ai utilisé l'article: https://codeburst.io/the-only-way-to-detect-touch-with-javascript-7791a3346685 (de David Gilbertson)

Il dispose de moyens très pratiques pour détecter les dispositifs tactiles ou en vol stationnaire.

Donc, a combiné les deux auteurs avec un peu de 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);
               });
             });
          });
      };

}); Cochez une fois si un périphérique autorise un survol. Si c'est le cas, introduisez la possibilité de survoler en utilisant .show (). Si le périphérique n'autorise pas d'événement de survol, le fichier .show () n'est jamais introduit, vous obtenez donc un comportement d'amorçage naturel sur le périphérique tactile. 

Assurez-vous de supprimer tout CSS concernant les classes de survol de menu.

Cela m'a pris trois jours :) alors j'espère que cela aidera certains d'entre vous.

0
Mike

Cette solution allume et éteint

<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>

Pour désactiver le survol pour les menus de réduction de taille lg, ajoutez

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

Bootstrap 4 CSS uniquement

Aucune des réponses CSS ne fonctionne entièrement. Soit le menu déroulant reste ouvert après un clic, soit il existe un espace qui fait que le menu déroulant se cache avant que vous puissiez atteindre les liens du menu sur lesquels vous cliquez.

Voici la seule solution CSS:

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

Supprimez data-toggle=dropdown du balisage HTML pour empêcher le menu déroulant de rester ouvert en un clic. Utilisez mt-0 (margin-top: 0) pour éliminer l’écart au-dessus du menu et permettre de survoler les éléments de menu.

Démo https://www.codeply.com/go/awyU7VTIJf


Code complet:

   .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