强制CSS的解决方法:转换后悬停以更新(打开菜单) [英] Workaround to force CSS :hover to update after a transition (opening a menu)

查看:54
本文介绍了强制CSS的解决方法:转换后悬停以更新(打开菜单)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

解决此问题已有几个问题。我包括我有两个原因:


  • 它建议了一种可能的替代解决方案

  • 演示代码可能对想要模拟菜单的其他人有用

  • It suggests a possible alternative solution
  • The demo code may be useful to others who want to simulate a menu

CSS转换之后,用户必须先移动鼠标,然后现在鼠标下的元素会发现它位于中:hover 州。我创建了一个菜单式功能,可以打开以显示不同的选项。打开过渡结束时鼠标下的选项与过渡开始时鼠标下的选项不同。因此,我必须找到一种解决方法。

After a CSS transition, the user must move the mouse before the element that is now under the mouse will notice that it is in a :hover state. I have created a menu-like feature that slides open to show different options. The option under the mouse at the end of the opening transition is not the same as the one under the mouse at the start of the transition. I have thus had to find a workaround.

您可以找到 jsFiddle这里和下面的演示源。寻找替代方法(在三个地方)看看我做了什么。

You can find a jsFiddle here and the demo source below. Look for WORKAROUND (in three places) to see what I have done.

要查看问题,请将鼠标移到菜单然后将其保留在原位,而不移动它。浏览器认为的列表项:hover 将显示为蓝色。我的解决方法用 li.ignoreHover 类否决了 li:hover 规则。为了使变通方法不可见,我可以简单地使用标准背景颜色。相反,我使用蓝色来显示问题。

To see the issue, move the mouse over the menu and then leave it in place, without moving it. The list item that the browser thinks is :hover will appear in blue. My workaround overrules the li:hover rule with an li.ignoreHover class. To make the workaround invisible, I can simply use the standard background colour. Instead, I am using blue to make the issue visible.

我的问题:我注意到按下其中一个修饰符键(大写大写锁定 Ctrl 选项 / Alt Î在Mac上,...)也会强制:hover 状态进行更新。有没有办法将这样的事件发送到 #menu 元素?

My question: I have noticed that pressing one of the modifier keys (Caps, Caps lock, Ctrl, Option/Alt, on Mac, ...) will also force the :hover state to update. Is there a way to send such an event to the #menu element?

(我试图这样做了没有成功,所以我更倾向于给你我的工作解决方案而不是可能无效的工作方式。)

(My attempts to do so have not been successful, so I prefer to give you my working workaround than one that may not be valid).

<!DOCTYPE html>
<html>
<head>
 <style>
#menu {
  position: relative;
  background: #ccc;
  display: inline-block;
}
#wrapper {
  margin: 5px;
}
#logo {
  width: 150px;
  height: 50px;
  border: 1px solid #000;
  margin: 0px auto;
  z-index: 10;
}
nav {
  width: 100%;
  overflow: hidden;
  text-align:center;
  height: 2em;
}
ul {
  position: relative;
  display:inline-block;
  margin: 0 auto;
  padding: 0;
  list-style-type: none;
  text-align:left;
}
li {
  display: block;
  margin: 0;
  padding:0.25em 0;
  line-height: 1.5em;
}
ul.animated, nav {
  transition: all 500ms linear 1s;
}
#menu.hover ul, #menu.hover nav {
  transition-delay: 0s;
}
li:hover,
li.hover {
  background-color: #999;
}
li.ignoreHover {
  background-color: #ccf; /* a touch of blue, so you can see it */
}
.selected {
  color: #fff;
}
 </style>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> 
</head>
<body>
<div id="menu">
  <div id="wrapper">
    <div id="logo"></div>
  </div>
  <nav>
    <ul>
      <li>Note one</li>
      <li>Note two</li>
      <li>Note three</li>
      <li>Not four much longer</li>
      <li>Note five</li>
      <li>Note six</li>
    </ul>
  </nav>
</div>

<script>
var test = {}
;(function createMenu() {
    var item = 3;
    var minPadding = 5;
    var hover = "hover" // class

    var $li = $("li");
    var $ul = $("ul");
    var $menu = $("#menu");
    var $nav = $("nav");

    var itemHeight = parseInt($li.outerHeight(), 10);
    var itemCount = $ul.children().length;
    var menuWidth = $menu.outerWidth(true);
    var padding = (menuWidth - $ul.width()) / 2;

    var transitionDone = false;
    var mouseOver = false;
    var top;

    // Pad the list items to fill the width of the menu
    if (padding < minPadding) {
        // Widen the menu to allow for the minimum padding
        menuWidth += (minPadding - padding) * 2;
        $menu.width(menuWidth);
        padding = minPadding;
    }

    $li.css({
        paddingLeft: padding,
        paddingRight: padding
    });

    // Scroll to the current selected item
    selectItem(true);

    function selectItem(scroll) {
        $ul.children().removeClass("selected");
        $ul.children().eq(item).addClass("selected");

        if (scroll) {
            top = -(itemHeight * item);
            $ul.css({
                top: top
            });
        }
    }

    // Wait until the initial settings are applied 
    // before animating the transitions
    setTimeout(function () {
        $ul.addClass("animated");
    }, 1);

    // Handle interaction with the menu
    $menu.on("mouseover", openMenu);
    $menu.on("mouseleave", closeMenu);
    $menu.on("transitionend", menuIsOpen);
    $ul.on("click", treatClickOnItem);

    // <WORKAROUND...
    var x
    var y
    // ... WORKAROUND>


  function openMenu(event) {

        if (mouseOver) {
            // This method may be called multiple times as the menu is
            // transitioning to its open state
            return
        }

    // <WORKAROUND...
    $menu.on("mousemove", function updateXY(event) {
            x = event.pageX
            y = event.pageY
    })
    // ... WORKAROUND>

        $menu.addClass(hover);
        transitionDone = false;
        mouseOver = true;

        $nav.css({
            height: (itemHeight * itemCount)
        });
        $ul.css({
            top: 0
        });
    }

    function menuIsOpen() {
        transitionDone = true;

      // <WORKAROUND...   
    var $hover = $("li:hover").addClass("ignoreHover")
    var $item = $(document.elementFromPoint(x, y))
    if (mouseOver) {
      $item.addClass(hover)
    }
    $menu.on("mousemove", function () {
      $item.removeClass(hover)
      $hover.removeClass("ignoreHover")
      $menu.off("mousemove")
    })
        //... WORKAROUND>

        if (!mouseOver) {
            closeMenu()
        }
    }

    function closeMenu() {
        mouseOver = false;
        if (transitionDone) {
            $menu.removeClass(hover)

            $nav.css({
                height: itemHeight
            });
            $ul.css({
                top: top
            });
        }
  }

    function treatClickOnItem(event) {
        item = $(event.target).index();
        top = -(itemHeight * item);
        selectItem();
        // DO MORE STUFF WITH THE SELECTION
    }
})()
</script>
</body>
</html>


推荐答案

jsBin演示

似乎几乎不可能获取:悬停元素的动画状态。

Seems like it's almost impossible to get the :hover state of an element while it's animating.

删除:hover 来自CSS和

使用所需的样式创建 .hover 。使用jQuery切换 .hover

Remove that :hover from CSS and
create instead a class .hover with the desired styles. use jQuery to toggle .hover:

$links.hover(function(){
  $(this).toggleClass("hover");
});

现在回到您的问题:

为了在菜单打开后获得正确的元素突出显示

我们需要始终知道鼠标Y的位置:

In order to get the right element highlighted once the menu opens
we need to always know the mouse Y position:

var mouseY = 0; // Needed to know the mouse position when menu is opening
$(document).on("mousemove", function( e ){
    mouseY = e.clientY; // Update the Y value
});

现在,在折叠的菜单上悬停,使用jQuery动画菜单,

里面动画步骤回调通过定位匹配的每个链接位置,每个链接位置 .filter()来获取它们鼠标位置。

最后将 .hover 仅应用于那个:

now, on collapsed menu hover, animate your menu using jQuery,
inside the animate step callback get on every frame each link position, .filter() them by targeting the one that matches the mouse position.
And finally apply the .hover to only that one:

function openMenu() {
  $navUl.stop().animate({top: 0});
  $nav.stop().animate({height: linkH*nLinks}, {
    duration: 600,
    step: function( menuHeight ){
        // keeps removing and adding class during the animation time.
        // (it's an overkill but no other solution to that issue so far)
        $links.removeClass("hover").filter(function(i, e){
          var t = e.getBoundingClientRect().top;
          return mouseY > t  &&  mouseY < t+linkH;
        }).addClass("hover"); // only to the link returned by `.filter()` condition
    }
  });
}

!重要说明:上述过滤将与您拥有的许多项目一样昂贵,因为它会在每个动画帧中尝试获取位置。如果你发现缓慢 - 改善上述。

! important note: the above filtering will be as expensive as many items you have, cause at every animate frame it tries to get the positions. If you detect slowliness - improve the above.

回顾
每帧
检查鼠标 clientX / Y 坐标位于元素的元素内.getBoundingClientRect()坐标/值

To recap
at every frame check if the mouse clientX/Y coordinates are inside the element's element.getBoundingClientRect() coordinates/values

这篇关于强制CSS的解决方法:转换后悬停以更新(打开菜单)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆