How to implement a Navbar Dropdown Hover in Bootstrap v4?

前端 未结 20 871
猫巷女王i
猫巷女王i 2020-12-02 05:40

I am a bit confused on the new bootstrap version since they changed dropdown menus to divs:

相关标签:
20条回答
  • 2020-12-02 06:16

    Simple, CSS only solution:

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

    When clicked, it will still get the class show toggled to it (and will remain open when no longer hovered).


    To get around this properly is to use events and properties reserved to pointer based devices: jQuery's mouseenter, mouseleave and :hover. Should work smoothly, intuitively, while not interfering at all with how the dropdown works on touch based devices. Try it out, let me know if it works for you:

    Complete jQuery solution (touch untouched):

    Pre v4.1.2 solution (deprecated):

    $('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 introduced this change to how dropdowns work, making the solution above no longer work.
    Here's the up to date solution for having the dropdown open on hover in v4.1.2 and above:

    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>


    Important note: If using the jQuery solution, it is important to remove the CSS one (or the dropdown won't close when .dropdown-toggle is clicked or when an menu option is clicked).

    0 讨论(0)
  • 2020-12-02 06:17

    (June 2020) I found this solution and I thought I should post it here:

    Bootstrap version: 4.3.1

    The CSS part:

    .navbar .nav-item:not(:last-child) {
      margin-right: 35px;
    }
    
    .dropdown-toggle::after {
       transition: transform 0.15s linear;
    }
    
    .show.dropdown .dropdown-toggle::after {
      transform: translateY(3px);
    }
    
    .dropdown-menu {
      margin-top: 0;
    }
    

    The jQuery part:

    const $dropdown = $(".dropdown");
    const $dropdownToggle = $(".dropdown-toggle");
    const $dropdownMenu = $(".dropdown-menu");
    const showClass = "show";
    
    $(window).on("load resize", function() {
      if (this.matchMedia("(min-width: 768px)").matches) {
        $dropdown.hover(
          function() {
            const $this = $(this);
            $this.addClass(showClass);
            $this.find($dropdownToggle).attr("aria-expanded", "true");
            $this.children($dropdownMenu).addClass(showClass);
          },
          function() {
            const $this = $(this);
            $this.removeClass(showClass);
            $this.find($dropdownToggle).attr("aria-expanded", "false");
            $this.children($dropdownMenu).removeClass(showClass);
          }
        );
      } else {
        $dropdown.off("mouseenter mouseleave");
      }
    });
    

    Source: https://webdesign.tutsplus.com/tutorials/how-to-make-the-bootstrap-navbar-dropdown-work-on-hover--cms-33840

    0 讨论(0)
  • 2020-12-02 06:17

    Google brought me here but... The examples provided work if the dropdown menu is overlaping (at least by 1px) with its parent when show. If not, it loses focus and nothing works as intended.

    Here is a working solution with jQuery and Bootstrap 4.5.2 :

    $('li.nav-item').mouseenter(function (e) {
    
            e.stopImmediatePropagation();
    
            if ($(this).hasClass('dropdown')) {
    
                // target element containing dropdowns, show it
                $(this).addClass('show');
                $(this).find('.dropdown-menu').addClass('show');
    
                // Close dropdown on mouseleave
                $('.dropdown-menu').mouseleave(function (e) {
                    e.stopImmediatePropagation();
                    $(this).removeClass('show');
                });
    
                // If you have a prenav above, this clears open dropdowns (since you probably will hover the nav-item going up and it will reopen its dropdown otherwise)
                $('#prenav').off().mouseenter(function (e) {
                    e.stopImmediatePropagation();
                    $('.dropdown-menu').removeClass('show');
                });
    
            } else {
                // unset open dropdowns if hover is on simple nav element
                $('.dropdown-menu').removeClass('show');
            }
        });
    
    0 讨论(0)
  • 2020-12-02 06:21

    CSS and Desktop only solution

    @media (min-width: 992px) { 
    .dropdown:hover>.dropdown-menu {
      display: block;
    }
    }
    
    0 讨论(0)
  • 2020-12-02 06:26

    1. Remove data-toggle="dropdown" attribute (so click will not open dropdown menu)

    2. Add :hover pseudo-class to show dropdown-menu

    .dropdown:hover .dropdown-menu {display: block;}
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/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 dropdown">
            <a class="nav-link dropdown-toggle" href="http://example.com" id="navbarDropdownMenuLink" 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>
          <li class="nav-item">
            <a class="nav-link" href="#">Features</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">Pricing</a>
          </li>
        </ul>
      </div>
    </nav>

    0 讨论(0)
  • 2020-12-02 06:27

    Bootstrap v4 Solution - jQuery based, but better than a pure css solution

    This ensures that you can still follow top level link clicks and is compatible with mobile.

    This was built with desktop and mobile in mind. Fell free to wrap the jQuery with a conditional that checks if the window width is greater than 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
      }
    }
    
    0 讨论(0)
提交回复
热议问题