【问题标题】:Expanding hover area to outside element将悬停区域扩展到外部元素
【发布时间】:2017-01-29 10:41:56
【问题描述】:

我有一个下拉菜单,子菜单放置在不同的元素上。所以基本上当鼠标离开菜单项时,子菜单会立即关闭,因为子菜单不是子菜单。

var menuItem = $(".menu-item");


menuItem.hover(hoverIn, hoverOut);

function hoverIn() {
  var mnItemMeta = $(this)[0].getBoundingClientRect();

  $(".sub-menu").css({
    opacity: 1,
    left: mnItemMeta.left
  })
}

function hoverOut() {
  $(".sub-menu").css({
    opacity: 0
  })
}
html,body{background-color: #efefef;}
.menu {
  list-style: none;
  padding-left: 0;
  display: flex;
  justify-content: center;
}
a {
  display: block;
  padding: 10px 20px;
  text-decoration: none;
  color: inherit;
}
.sub-menu {
  opacity: 0;
  background-color: white;
  position: absolute;
  transition: .2s ease;
}
.sub-menu-list {
  list-style: none;
  padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu-item"><a href="#">Menu Item</a>
  </li>
</ul>
<div class="sub-menu">
  <ul class="sub-menu-list">
    <li><a href="#">Sub Menu 1</a>
    </li>
    <li><a href="#">Sub Menu 2</a>
    </li>
    <li><a href="#">Sub Menu 3</a>
    </li>
    <li><a href="#">Sub Menu 4</a>
    </li>
  </ul>
</div>

https://jsfiddle.net/yans_fied/6wj0of90/

问题是如何扩展悬停区域,所以当光标指向子菜单时它会忽略hoverOut动作。

注意:不要告诉我将子菜单放在菜单项中,我已经知道它是如何工作的。这是针对需要将子菜单放置在菜单项之外的不同情况。

【问题讨论】:

  • 你可以使用jquery
  • 你需要把被悬停的元素放大,或者把子菜单放在里面
  • 如果你的子菜单需要放在外面,那么,当用户指向子菜单链接时,如何打开子菜单。因此,子菜单需要位于它自己的主菜单中。
  • @Samir 你检查过我的sn-p吗?所以基本上它就像 tab ui,它持有 tab id 来打开 tabcontent。
  • 我遇到了您的问题,您的问题有解决方案。只需增加具有类menu-itemli 的底部填充。确保sub-menu 和 li 应该相互连接。但问题是,如果用户想点击子菜单,此时用户必须从父li出来,因此sub-menu会自动隐藏。你会增加多少你的父母li底部paddnig?可以吗?

标签: javascript jquery css hover jquery-events


【解决方案1】:

您可以将sub-menu 放在menu-item 中。

var menuItem = $(".menu-item");
menuItem.hover(hoverIn, hoverOut);

function hoverIn() {
  var mnItemMeta = $(this)[0].getBoundingClientRect();

  $(".sub-menu").css({
    opacity: 1,
    left: mnItemMeta.left
  })
}

function hoverOut() {
  $(".sub-menu").css({
    opacity: 0
  })
}
html, body {
  background-color: #efefef;
}
.menu {
  list-style: none;
  padding-left: 0;
  display: flex;
  justify-content: center;
}
a {
  display: block;
  padding: 10px 20px;
  text-decoration: none;
  color: inherit;
}
.sub-menu {
  opacity: 0;
  background-color: white;
  position: absolute;
  transition: .2s ease;
}
.sub-menu-list {
  list-style: none;
  padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<ul class="menu">
  <li class="menu-item"><a href="#">Menu Item</a>
    <div class="sub-menu">
      <ul class="sub-menu-list">
        <li><a href="#">Sub Menu 1</a></li>
        <li><a href="#">Sub Menu 2</a></li>
        <li><a href="#">Sub Menu 3</a></li>
        <li><a href="#">Sub Menu 4</a></li>
      </ul>
    </div>
  </li>
</ul>

另一种方法是检查.menu-item.sub-menuhover 状态。您需要在这里设置一点超时,以防止它提前关闭。

var timeout,
    hovered = false,
    menuItem = $(".menu-item, .sub-menu").hover(hoverIn, hoverOut);;

function hoverIn() {
    hovered = true;

    var mnItemMeta = this.getBoundingClientRect();

    $(".sub-menu").show().css({
        opacity: 1,
        left: mnItemMeta.left,
    });
}

function hoverOut() {
  hovered = false;

    clearTimeout(timeout);
    timeout = setTimeout(function() {
        if (!hovered) {
            $(".sub-menu").css({
                opacity: 0,
            }).hide()
        }
    }, 100);
}
html, body {
  background-color: #efefef;
}
.menu {
  list-style: none;
  padding-left: 0;
  display: flex;
  justify-content: center;
}
a {
  display: block;
  padding: 10px 20px;
  text-decoration: none;
  color: inherit;
}
.sub-menu {
  opacity: 0;
  background-color: white;
  position: absolute;
  transition: .2s ease;
}
.sub-menu-list {
  list-style: none;
  padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<ul class="menu">
  <li class="menu-item"><a href="#">Menu Item</a></li>
</ul>
<div class="sub-menu">
  <ul class="sub-menu-list">
    <li><a href="#">Sub Menu 1</a></li>
    <li><a href="#">Sub Menu 2</a></li>
    <li><a href="#">Sub Menu 3</a></li>
    <li><a href="#">Sub Menu 4</a></li>
  </ul>
</div>

【讨论】:

  • @eisnehr 我已经知道了,但这是针对不同的布局,子菜单需要放在菜单项之外。 :)
  • 我用第二个例子扩展了我的答案@ArionaRian
  • 嗨@eisnehr,你能帮我检查一下吗? jsfiddle.net/yans_fied/6wj0of90 看来需要更多解决方法
【解决方案2】:

如果您将您的第一行 js 更改为:var menuItem = $(".menu-item, .sub-menu");,然后将top: 3em; 添加到.menu-list css(以提供与.menu div 的微小重叠并消除闪烁)那么一切都应该很好。

var menuItem = $(".menu-item, .sub-menu");


menuItem.hover(hoverIn, hoverOut);

function hoverIn() {
  var mnItemMeta = $(this)[0].getBoundingClientRect();

  $(".sub-menu").css({
    opacity: 1,
    left: mnItemMeta.left
  })
}

function hoverOut() {
  $(".sub-menu").css({
    opacity: 0
  })
}
html,body{background-color: #efefef;}
.menu {
  list-style: none;
  padding-left: 0;
  display: flex;
  justify-content: center;
}
a {
  display: block;
  padding: 10px 20px;
  text-decoration: none;
  color: inherit;
}
.sub-menu {
  opacity: 0;
  background-color: white;
  position: absolute;
  transition: .2s ease;
  top: 3em;
}
.sub-menu-list {
  list-style: none;
  padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu-item"><a href="#">Menu Item</a>
  </li>
</ul>
<div class="sub-menu">
  <ul class="sub-menu-list">
    <li><a href="#">Sub Menu 1</a>
    </li>
    <li><a href="#">Sub Menu 2</a>
    </li>
    <li><a href="#">Sub Menu 3</a>
    </li>
    <li><a href="#">Sub Menu 4</a>
    </li>
  </ul>
</div>

【讨论】:

  • @ArionaRian,快速尝试了新小提琴jsfiddle.net/manoeuvres/73452ken 菜单触发不可靠,部分原因是三角形的关联方式,但您可能希望保留其他一些调整为下一次迭代。
【解决方案3】:

你可以添加

.sub-menu::before{
     content:'';
     height: <height of menu item>
     width: 100%;
     position:absolute;
     bottom:100%;
}

并将hoverOut 放在.sub-menu 上。

【讨论】:

    【解决方案4】:

    这里是一个例子

    1. 子菜单中添加了一个伪元素,以为悬停提供重叠区域。黄色仅用于演示目的。
    2. 悬停在菜单和子菜单之外仅设置一个变量。子菜单隐藏在一个单独的函数中,用于评估变量。需要稍微超时以允许从一个更改为另一个。

    var menuItem = $(".menu-item");
    var submenuItem = $(".sub-menu");
    
    var hoverMenu = false;
    var hoverSubmenu = false;
    
    
    menuItem.hover(hoverIn, hoverOut);
    
    function hoverIn() {
      hoverMenu = true;
      var mnItemMeta = $(this)[0].getBoundingClientRect();
    
      $(".sub-menu").css({
        opacity: 1,
        left: mnItemMeta.left
      })
    }
    
    function hoverOut() {
      hoverMenu = false;
      setTimeout (hide, 10);
    }
    
    submenuItem.hover(hoverSmIn, hoverSmOut);
    
    function hoverSmIn() {
      hoverSubmenu = true;
    }
    
    function hoverSmOut() {
      hoverSubmenu = false;
      setTimeout (hide, 10);
    }
    
    function hide() {
      if (hoverMenu == false && hoverSubmenu == false) {
        $(".sub-menu").css({
          opacity: 0
        })
      }
    }
    html,body{background-color: #efefef;}
    .menu {
      list-style: none;
      padding-left: 0;
      display: flex;
      justify-content: center;
    }
    a {
      display: block;
      padding: 10px 20px;
      text-decoration: none;
      color: inherit;
    }
    .sub-menu {
      opacity: 0;
      background-color: white;
      position: absolute;
      transition: .2s ease;
    }
    .sub-menu:before {
      content: "";
      position: absolute;
      width: 100%;
      left: 0px;
      bottom: 100%;
      height: 26px;
      background-color: yellow;
    }
    .sub-menu-list {
      list-style: none;
      padding-left: 0;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <ul class="menu">
      <li class="menu-item"><a href="#">Menu Item</a>
      </li>
    </ul>
    <div class="sub-menu">
      <ul class="sub-menu-list">
        <li><a href="#">Sub Menu 1</a>
        </li>
        <li><a href="#">Sub Menu 2</a>
        </li>
        <li><a href="#">Sub Menu 3</a>
        </li>
        <li><a href="#">Sub Menu 4</a>
        </li>
      </ul>
    </div>

    【讨论】:

      【解决方案5】:

      我今天在你的剧本上玩了一会儿,从你的 Fiddle 开始,而不是部分 sn-p...

      你是如此接近......
      但问题是你有两个不同的父元素类要处理(阅读:绑定到它们的事件处理程序)......并且处理方式不同。

      当您将鼠标从打开子菜单的元素移动到另一个应该保持打开状态的元素时,某些事件不应触发。仅当鼠标没有进入另一个 menu___itemdropdown-menu__content“足够快”时,才会发生 mouseout 事件。

      mouseentermouseout 在触发时非常...比人类鼠标移动还要快。

      100 毫秒的小延迟在这里很有用。

      setTimeout() 在离开这些元素时将 dropdown-holder 设置为 display:none,在进入时设置 clearTimeout

      $(".menu__item").hover(
        function() {
          $(".dropdown-holder").css({"display":"block"});
          displaySubMenu( $(this) );
          clearTimeout(NavDelay);
        },
        function(){
          setNavDelay();
        });
      
      $(".dropdown-menu__content").hover(
        function() {
          clearTimeout(NavDelay);
        },
        function(){
          setNavDelay();
        });
      

      setTimout 函数很简单:

      function setNavDelay(){
        NavDelay = setTimeout(function(){
              $(".dropdown-holder").css({"display":"none"});
          },100);
      }
      

      这里是子菜单显示功能,没怎么修改:

      function displaySubMenu(element){
        var itemMeta = element[0].getBoundingClientRect();
        //console.log( itemMeta );
        var subID = element.data('sub');
        console.log(subID);
      
        var subCnt = $(subID).find(".dropdown-menu__content").css({"display":"block"});
        var subMeta = subCnt[0].getBoundingClientRect();
        //console.log( subMeta );
        var subCntBtm = subCnt.find(".bottom-section");
      
        menuHoveredID = subID;  // Let's Keep this info in memory in a var that has global scope
      
        $(drBg).css({
          "display":"block",
          "left": itemMeta.left - ((subMeta.width / 2) - itemMeta.width / 2),
          "width": subMeta.width,
          "height": subMeta.height
        });
        $(drBgBtm).css({
          "top": subCntBtm.position().top
        });
        $(drArr).css({
          "display":"block",
          "left": itemMeta.left + itemMeta.width / 2 - 10
        });
        $(drCnt).css({
          "display":"block",
          "left": itemMeta.left - ((subMeta.width / 2) - itemMeta.width / 2),
          "width": subMeta.width,
          "height": subMeta.height
        });
      
        // Ensure the right content is displayed
        $(".dropdown-menu__content").css({
          "display":"none"
        });
        $(menuHoveredID).find(".dropdown-menu__content").css({
          "display":"block"
        });
      }
      

      为确保显示正确的内容,menuHoveredID 变量通过menu__item hovermouseenter 处理程序提供给函数。

      您的 onload 声明:

      var dr = $(".dropdown__content"),
          drBg = $(".dropdown__bg"),
          drBgBtm = $(".dropdown__bg-bottom"),
          drArr = $(".dropdown__arrow"),
          drMenu = $(".dropdown-menu__content"),
          drCnt = $(".dropdown__content"),
      
          menuHoveredID ="",
          NavDelay;
      

      我去掉了不必要的东西并添加了两个变量...
      如果您注意到,我还更正了分号/逗号... ;)

      Working CodePen here

      【讨论】:

      • 感谢您提供如此详尽的答案,但在您发布之前我已经弄清楚了:D,setTimeout 确实可以解决这种情况。
      • 好。另一件事我忘了提,我使用 display:none 而不是 opacity:0 因为子菜单中的链接即使在不可见的情况下仍然可以点击(在 Chrome 中,可能不是所有浏览器)......你会看到手形指针如果你寻找它。 ;)
      • 可以用pointer-events: none 修复或者切换z-index :)
      【解决方案6】:

      好的,我已经找到了解决方案。感谢大家提供一些建议,但我不能接受你们的答案,因为解决方案需要更多的解决方法。

      所以,我基本上创建了两个函数,即startCloseTimeout()stopCloseTimout(),并将其绑定到menu-itemsubmenu

      这是函数本身:

      var startCloseTimeout = function (){
          closeDropdownTimeout = setTimeout( () => closeDropdown() , 50 );
      },
      
      stopCloseTimeout   = function () {
          clearTimeout( closeDropdownTimeout );
      };
      

      这是我绑定鼠标事件的方式:

      //- Binding mouse event to each menu items
      menuItems.forEach( el => {
      
          //- mouse enter event
          el.addEventListener( 'mouseenter', function() {
              stopCloseTimeout();
              openDropdown( this );
          }, false );
      
          //- mouse leave event
          el.addEventListener( 'mouseleave', () => startCloseTimeout(), false);
      
      } );
      
      //- Binding mouse event to each sub menus
      menuSubs.forEach( el => {
      
          el.addEventListener( 'mouseenter', () => stopCloseTimeout(), false );
          el.addEventListener( 'mouseleave', () => startCloseTimeout(), false );
      
      } );
      

      那么,这段代码是如何工作的?

      通过创建关闭超时处理程序,我们可以控制下拉菜单何时关闭。

      当鼠标进入菜单项时,会发生以下情况: 1.停止当前运行closeDropdownTimout 2.打开相关下拉菜单

      当鼠标离开菜单项时,它会启动closeDropdownTimout

      但是下拉菜单怎么仍然打开?由于我们在下拉菜单上设置了相同的操作,closeDropdownTimout 将被清除并取消关闭操作。

      有关完整源代码,您可以在 codepen http://codepen.io/ariona/pen/pENkXW 上查看它

      谢谢。

      【讨论】:

        猜你喜欢
        • 2019-04-15
        • 1970-01-01
        • 1970-01-01
        • 2015-08-28
        • 2011-02-01
        • 2014-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多