Изменение состояния выпадающего меню / подменю bootstrap

Я новичок в раскрывающихся функциях на сайте и в настоящее время работаю над приложением, которое использует раскрывающиеся меню Bootstrap с подменю. Есть ли способ с текущей иерархией меню / подменю начальной загрузки удерживать активное состояние даже при перемещении мыши до тех пор, пока вы не щелкните за пределами области?

Вот мой css:

<style>
        li.dropdown-submenu a#shoppingMenuLabel{
            transition-timing-function: ease-in-out 1s;
            -moz-transition-timing-function: ease-in-out 1s;
            -o-transition-timing-function: ease-in-out 1s;
            -webkit-transition-timing-function: ease-in-out 1s;
            transition-duration: 1s;
        }

        .fade {
                opacity: 1;
                transition: opacity .40s ease-in-out;
                -moz-transition: opacity .40s ease-in-out;
                -webkit-transition: opacity .40s ease-in-out;
        }

        #imageContainer img{
            width: 427px;            
            background-repeat: no-repeat;      
        }

        .dropdown-submenu {
            position: initial;
        }

        ul.dropdown-menu{
            transition-timing-function: ease-in 2s;
            -moz-transition-timing-function: ease-in 2s;
            -o-transition-timing-function: ease-in 2s;
            -webkit-transition-timing-function: ease-in s;
            transition-duration: 1s;
        }

        ul.dropdown-menu li a{
            transition-timing-function: ease-out .50s;
            -moz-transition-timing-function: ease-out .50s;
            -o-transition-timing-function: ease-out .50s;
            -webkit-transition-timing-function: ease-out .50s;
            transition-duration: .50s;
        }

        ul.dropdown-menu li.dropdown-submenu{
            transition-timing-function: ease-out 3s;
            -moz-transition-timing-function: ease-out 3s;
            -o-transition-timing-function: ease-out 3s;
            -webkit-transition-timing-function: ease-out 3s;
            transition-duration: 3s;
        }        

        ul.dropdown-menu li.dropdown-submenu a:hover{
            font-weight: bold;
            display:block
        }

        li.dropdown-submenu a ul.dropdown-menu{
        transition: ease-out;
        }

        .dropdown-content a:hover{
            background: transparent;
            font-weight: bold;
        }

        li.dropdown-submenu a:hover{
            font-weight: bold !important; 
        }

        .dropdown-submenu>.dropdown-menu {
            top: 0;
            left: 95%;
            margin-top: -6px;
            margin-left: -1px;
            padding-left: 10px;
            border: 0;
            border-left: 2px solid #f1f1f1 !important;
        }

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

        .dropdown-submenu>a:after {
            display: block;
            content: " ";
            float: right;
            width: 0;
            height: 0;
            border-color: transparent;
            border-style: solid;
            border-width: 5px 0 5px 5px;
            border-left-color: searchResults.htmlccc;
            margin-top: 5px;
            margin-right: 0px;
        }

        .dropdown-submenu:hover>a:after {
            border-left-color: searchResults.htmlfff;
        }

        .dropdown-submenu.pull-left {
            float: none;
        }

        .dropdown-submenu.pull-left>.dropdown-menu {
            left: -100%;
            margin-left: 10px;
            border: 0;
        }

        ul.mainDropDown {
            margin: 0;
            padding: 0;
        }

        ul.mainDropDown li {
            list-style: none;
        }

        ul.dropdown-menu {
            width: 285px;
        }

        li.dropdown-submenu a {
            display: block;
        }

        @media only screen and (max-width: 800px) {
            ul.dropdown-menu {
                width: 150px;
            }
        }

        ul.dropdown-menu > li > a.maintainHover {
            color: white;
            background-color: #0081C2;
        }

    </style>

Вот HTML

    <!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>
.dropdown-submenu {
    position: relative;
}

.dropdown-submenu .dropdown-menu {
    top: 0;
    left: 100%;
    margin-top: -1px;
}
</style>
</head>
<body>

<div class="container">
  <h2>Multi-Level Dropdowns</h2>
  <p>In this example, we have created a .dropdown-submenu class for multi-level dropdowns (see style section above).</p>
  <p>Note that we have added jQuery to open the multi-level dropdown on click (see script section below).</p>                                        
  <div class="dropdown">
    <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">Tutorials
    <span class="caret"></span></button>
    <ul class="dropdown-menu">
      <li><a tabindex="-1" href="#">HTML</a></li>
      <li><a tabindex="-1" href="#">CSS</a></li>
      <li class="dropdown-submenu">
        <a class="test" tabindex="-1" href="#">New dropdown <span class="caret"></span></a>
        <ul class="dropdown-menu">
          <li><a tabindex="-1" href="#">2nd level dropdown</a></li>
          <li><a tabindex="-1" href="#">2nd level dropdown</a></li>
          <li class="dropdown-submenu">
            <a class="test" href="#">Another dropdown <span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href="#">3rd level dropdown</a></li>
              <li><a href="#">3rd level dropdown</a></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </div>
</div>

<script>
$(document).ready(function(){
  $('.dropdown-submenu a.test').on("click", function(e){
    $(this).next('ul').toggle();
    e.stopPropagation();
    e.preventDefault();
  });
});
</script>

</body>
</html>

вот код jQuery внутри html:

var $menu = $(".dropdown-menu");

        // jQuery-menu-aim: <meaningful part of the example>
        // Hook up events to be fired on menu row activation.
        $menu.menuAim({
            activate: activateSubmenu,
            deactivate: deactivateSubmenu
        });
        // jQuery-menu-aim: </meaningful part of the example>

        // jQuery-menu-aim: the following JS is used to show and hide the submenu
        // contents. Again, this can be done in any number of ways. jQuery-menu-aim
        // doesn't care how you do this, it just fires the activate and deactivate
        // events at the right times so you know when to show and hide your submenus.
        function activateSubmenu(row) {
            var $row = $(row),
                submenuId = $row.data("submenuId"),
                $submenu = $("#" + submenuId),
                height = $menu.outerHeight(),
                width = $menu.outerWidth();

            // Show the submenu
            $submenu.css({
                display: "block",
                top: -1,
                left: width - 3, // main should overlay submenu
                height: height - 4 // padding for main dropdown's arrow
            });

            // Keep the currently activated row's highlighted look
            $row.find("a").addClass("maintainHover");
        }

        function deactivateSubmenu(row) {
            var $row = $(row),
                submenuId = $row.data("submenuId"),
                $submenu = $("#" + submenuId);

            // Hide the submenu and remove the row's highlighted look
            $submenu.css("display", "none");
            $row.find("a").removeClass("maintainHover");
        }

        // Bootstrap's dropdown menus immediately close on document click.
        // Don't let this event close the menu if a submenu is being clicked.
        // This event propagation control doesn't belong in the menu-aim plugin
        // itself because the plugin is agnostic to bootstrap.
        $(".dropdown-menu li").hover(function (e) {
            e.stopPropagation();
        });

        $(document).click(function () {
            // Simply hide the submenu on any click. Again, this is just a hacked
            // together menu/submenu structure to show the use of jQuery-menu-aim.
            $(".popover").css("display", "none");
            $("a.maintainHover").removeClass("maintainHover");
        });

Ниже находится меню библиотеки jQuery Цель и URL Пример URL: https://rawgit.com/kamens/jQuery-menu-aim/master/example/example.html#

/**
 * menu-aim is a jQuery plugin for dropdown menus that can differentiate
 * between a user trying hover over a dropdown item vs trying to navigate into
 * a submenu's contents.
 *
 * menu-aim assumes that you have are using a menu with submenus that expand
 * to the menu's right. It will fire events when the user's mouse enters a new
 * dropdown item *and* when that item is being intentionally hovered over.
 *
 * __________________________
 * | Monkeys  >|   Gorilla  |
 * | Gorillas >|   Content  |
 * | Chimps   >|   Here     |
 * |___________|____________|
 *
 * In the above example, "Gorillas" is selected and its submenu content is
 * being shown on the right. Imagine that the user's cursor is hovering over
 * "Gorillas." When they move their mouse into the "Gorilla Content" area, they
 * may briefly hover over "Chimps." This shouldn't close the "Gorilla Content"
 * area.
 *
 * This problem is normally solved using timeouts and delays. menu-aim tries to
 * solve this by detecting the direction of the user's mouse movement. This can
 * make for quicker transitions when navigating up and down the menu. The
 * experience is hopefully similar to amazon.com/'s "Shop by Department"
 * dropdown.
 *
 * Use like so:
 *
 *      $("#menu").menuAim({
 *          activate: $.noop,  // fired on row activation
 *          deactivate: $.noop  // fired on row deactivation
 *      });
 *
 *  ...to receive events when a menu's row has been purposefully (de)activated.
 *
 * The following options can be passed to menuAim. All functions execute with
 * the relevant row's HTML element as the execution context ('this'):
 *
 *      .menuAim({
 *          // Function to call when a row is purposefully activated. Use this
 *          // to show a submenu's content for the activated row.
 *          activate: function() {},
 *
 *          // Function to call when a row is deactivated.
 *          deactivate: function() {},
 *
 *          // Function to call when mouse enters a menu row. Entering a row
 *          // does not mean the row has been activated, as the user may be
 *          // mousing over to a submenu.
 *          enter: function() {},
 *
 *          // Function to call when mouse exits a menu row.
 *          exit: function() {},
 *
 *          // Selector for identifying which elements in the menu are rows
 *          // that can trigger the above events. Defaults to "> li".
 *          rowSelector: "> li",
 *
 *          // You may have some menu rows that aren't submenus and therefore
 *          // shouldn't ever need to "activate." If so, filter submenu rows w/
 *          // this selector. Defaults to "*" (all elements).
 *          submenuSelector: "*",
 *
 *          // Direction the submenu opens relative to the main menu. Can be
 *          // left, right, above, or below. Defaults to "right".
 *          submenuDirection: "right"
 *      });
 *
 * https://github.com/kamens/jQuery-menu-aim
 */
(function($) {
  $.fn.menuAim = function(opts) {
    // Initialize menu-aim for all elements in jQuery collection
    this.each(function() {
      init.call(this, opts);
    });

    return this;
  };

  function init(opts) {
    var $menu = $(this),
      activeRow = null,
      mouseLocs = [],
      lastDelayLoc = null,
      timeoutId = null,
      options = $.extend(
        {
          rowSelector: "> li",
          submenuSelector: "*",
          submenuDirection: "right",
          tolerance: 75, // bigger = more forgivey when entering submenu
          enter: $.noop,
          exit: $.noop,
          activate: $.noop,
          deactivate: $.noop,
          exitMenu: $.noop
        },
        opts
      );

    var MOUSE_LOCS_TRACKED = 3, // number of past mouse locations to track
      DELAY = 300; // ms delay when user appears to be entering submenu

    /**
     * Keep track of the last few locations of the mouse.
     */
    var mousemoveDocument = function(e) {
      mouseLocs.push({
        x: e.pageX,
        y: e.pageY
      });

      if (mouseLocs.length > MOUSE_LOCS_TRACKED) {
        mouseLocs.shift();
      }
    };

    /**
     * Cancel possible row activations when leaving the menu entirely
     */
    var mouseleaveMenu = function() {
      if (timeoutId) {
        clearTimeout(timeoutId);
      }

      // If exitMenu is supplied and returns true, deactivate the
      // currently active row on menu exit.
      if (options.exitMenu(this)) {
        if (activeRow) {
          options.deactivate(activeRow);
        }

        activeRow = null;
      }
    };

    /**
     * Trigger a possible row activation whenever entering a new row.
     */
    var mouseenterRow = function() {
        if (timeoutId) {
          // Cancel any previous activation delays
          clearTimeout(timeoutId);
        }

        options.enter(this);
        possiblyActivate(this);
      },
      mouseleaveRow = function() {
        options.exit(this);
      };

    /*
         * Immediately activate a row if the user clicks on it.
         */
    var hoverRow = function() {
      activate(this);
    };

    /**
     * Activate a menu row.
     */
    var activate = function(row) {
      if (row == activeRow) {
        return;
      }

      if (activeRow) {
        options.deactivate(activeRow);
      }

      options.activate(row);
      activeRow = row;
    };

    /**
     * Possibly activate a menu row. If mouse movement indicates that we
     * shouldn't activate yet because user may be trying to enter
     * a submenu's content, then delay and check again later.
     */
    var possiblyActivate = function(row) {
      var delay = activationDelay();

      if (delay) {
        timeoutId = setTimeout(function() {
          possiblyActivate(row);
        }, delay);
      } else {
        activate(row);
      }
    };

    /**
     * Return the amount of time that should be used as a delay before the
     * currently hovered row is activated.
     *
     * Returns 0 if the activation should happen immediately. Otherwise,
     * returns the number of milliseconds that should be delayed before
     * checking again to see if the row should be activated.
     */
    var activationDelay = function() {
      if (!activeRow || !$(activeRow).is(options.submenuSelector)) {
        // If there is no other submenu row already active, then
        // go ahead and activate immediately.
        return 0;
      }

      var offset = $menu.offset(),
        upperLeft = {
          x: offset.left,
          y: offset.top - options.tolerance
        },
        upperRight = {
          x: offset.left + $menu.outerWidth(),
          y: upperLeft.y
        },
        lowerLeft = {
          x: offset.left,
          y: offset.top + $menu.outerHeight() + options.tolerance
        },
        lowerRight = {
          x: offset.left + $menu.outerWidth(),
          y: lowerLeft.y
        },
        loc = mouseLocs[mouseLocs.length - 1],
        prevLoc = mouseLocs[0];

      if (!loc) {
        return 0;
      }

      if (!prevLoc) {
        prevLoc = loc;
      }

      if (
        prevLoc.x < offset.left ||
        prevLoc.x > lowerRight.x ||
        prevLoc.y < offset.top ||
        prevLoc.y > lowerRight.y
      ) {
        // If the previous mouse location was outside of the entire
        // menu's bounds, immediately activate.
        return 0;
      }

      if (lastDelayLoc && loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) {
        // If the mouse hasn't moved since the last time we checked
        // for activation status, immediately activate.
        return 0;
      }

      // Detect if the user is moving towards the currently activated
      // submenu.
      //
      // If the mouse is heading relatively clearly towards
      // the submenu's content, we should wait and give the user more
      // time before activating a new row. If the mouse is heading
      // elsewhere, we can immediately activate a new row.
      //
      // We detect this by calculating the slope formed between the
      // current mouse location and the upper/lower right points of
      // the menu. We do the same for the previous mouse location.
      // If the current mouse location's slopes are
      // increasing/decreasing appropriately compared to the
      // previous's, we know the user is moving toward the submenu.
      //
      // Note that since the y-axis increases as the cursor moves
      // down the screen, we are looking for the slope between the
      // cursor and the upper right corner to decrease over time, not
      // increase (somewhat counterintuitively).
      function slope(a, b) {
        return (b.y - a.y) / (b.x - a.x);
      }

      var decreasingCorner = upperRight,
        increasingCorner = lowerRight;

      // Our expectations for decreasing or increasing slope values
      // depends on which direction the submenu opens relative to the
      // main menu. By default, if the menu opens on the right, we
      // expect the slope between the cursor and the upper right
      // corner to decrease over time, as explained above. If the
      // submenu opens in a different direction, we change our slope
      // expectations.
      if (options.submenuDirection == "left") {
        decreasingCorner = lowerLeft;
        increasingCorner = upperLeft;
      } else if (options.submenuDirection == "below") {
        decreasingCorner = lowerRight;
        increasingCorner = lowerLeft;
      } else if (options.submenuDirection == "above") {
        decreasingCorner = upperLeft;
        increasingCorner = upperRight;
      }

      var decreasingSlope = slope(loc, decreasingCorner),
        increasingSlope = slope(loc, increasingCorner),
        prevDecreasingSlope = slope(prevLoc, decreasingCorner),
        prevIncreasingSlope = slope(prevLoc, increasingCorner);

      if (
        decreasingSlope < prevDecreasingSlope &&
        increasingSlope > prevIncreasingSlope
      ) {
        // Mouse is moving from previous location towards the
        // currently activated submenu. Delay before activating a
        // new menu row, because user may be moving into submenu.
        lastDelayLoc = loc;
        return DELAY;
      }

      lastDelayLoc = null;
      return 0;
    };

    /**
     * Hook up initial menu events
     */
    $menu
      .mouseleave(mouseleaveMenu)
      .find(options.rowSelector)
      .mouseenter(mouseenterRow)
      .mouseleave(mouseleaveRow)
      .hover(hoverRow);

    $(document).mousemove(mousemoveDocument);
  }
})(jQuery);

вы можете включить свой код? что ты пробовал?

95faf8e76605e973 13.09.2018 21:02

Вы можете стилизовать меню / подменю с помощью настраиваемого CSS. Если вы предоставите часть своего кода, будет более понятно.

Llazar 13.09.2018 21:03

@ 95faf8e76605e973 Я только что обновил код примером, который нашел в Интернете. URL-адрес действительно может дать вам большую часть того, что я опубликовал, но с использованием их версии разметки. Пример HTML выше - это разметка, над которой я работаю.

Justin Williams 13.09.2018 21:32

@JustinWilliams благодарим вас за включение кода. вы хотите сохранить hover элемента до тех пор, пока не будет щелкнуть снаружи?

95faf8e76605e973 13.09.2018 21:35

@Llazar Я только что добавил свой код. У меня выпадающее всплывающее меню работает нормально. Все, что я хочу, - это иметь возможность, чтобы родительский и любые другие предыдущие теги оставались активными после того, как я наведу на них курсор, пока я не выберу из раскрывающегося меню.

Justin Williams 13.09.2018 21:35

@ 95faf8e76605e973 ТОЧНО, лол

Justin Williams 13.09.2018 21:35

когда я пытаюсь эмулировать пример кода для библиотеки меню Aim, я получаю следующую ошибку: shopping.html: 1485 Uncaught TypeError: $ menu.menuAim не является функцией в shopping.html: 1485 (анонимно) @ shopping.html: 1485

Justin Williams 13.09.2018 21:45
0
7
1 466
1

Ответы 1

По умолчанию фон для пункта меню начальной загрузки - #f8f9fa
. Так что просто сохраните это в переменной для дальнейшего использования. Чтобы сохранить цвет при наведении на него, используйте

$('.dropdown-item').mouseover(function(){
    $('.dropdown-item').css("background", defaultColor); // reset previously hovered item
    $(this).css("background", "#f8f9fa");
});

Демо:

$(document).ready(function(){
    var defaultColor = $('.dropdown-item').css("background");
    $('.dropdown-item').mouseover(function(){
        $('.dropdown-item').css("background", defaultColor);
        $(this).css("background", "#f8f9fa");
    });

    $(document).click(function(e){
        $('.dropdown-item').css("background", defaultColor); // reset background
    });
});
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

<div class="dropdown">
  <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    Dropdown button
  </button>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
    <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>
</div>

В основном я стремился просто сохранить активное состояние меню, а не обязательно фон. Таким образом, при наведении указателя мыши сохраняйте состояние даже на дочернем уровне и удаляйте это состояние только щелчком из раскрывающегося списка.

Justin Williams 13.09.2018 21:54

Оцените пример, но, как и на главной странице Home Depot или Amazon, или на предоставленный мной URL-адрес, я хотел просто навести курсор на раскрывающийся список с дочерними узлами, которые при наведении курсора активируют список и сохраняют состояние наведения, пока я не щелкну. Это не второстепенная проблема.

Justin Williams 13.09.2018 21:56

@JustinWilliams ах, хорошо, я неправильно понял, в любом случае это та же концепция, просто добавьте класс, чтобы пометить его как активный. если вы можете сделать исполняемый фрагмент кода из своего примера, я могу сделать другой код, чтобы помочь объяснить и продемонстрировать

95faf8e76605e973 13.09.2018 22:13

Я пошел дальше и нашел фрагмент демонстрационного кода, который должен работать в тестовых целях, который я поместил в раздел html. Пожалуйста, дай мне знать.

Justin Williams 14.09.2018 01:45

Другие вопросы по теме