强制CSS的解决方法:转换后悬停以更新(打开菜单) [英] Workaround to force CSS :hover to update after a transition (opening a menu)
问题描述
解决此问题已有几个问题。我包括我有两个原因:
- 它建议了一种可能的替代解决方案
- 演示代码可能对想要模拟菜单的其他人有用
- 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>
推荐答案
似乎几乎不可能获取:悬停
元素的动画状态。
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屋!