CSS 如何防止触摸设备上按钮的粘性悬停效果
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17233804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to prevent sticky hover effects for buttons on touch devices
提问by Chris
I have created a carousel with a previous and a next button that are always visible. These buttons have a hover state, they turn blue. On touch devices, like iPad, the hover state is sticky, so the button stays blue after tapping it. I don't want that.
我创建了一个带有始终可见的上一个和下一个按钮的轮播。这些按钮有一个悬停状态,它们变成蓝色。在 iPad 等触控设备上,悬停状态是粘性的,因此点击按钮后按钮会保持蓝色。我不想要那个。
I could add a
no-hover
classontouchend
for each button, and make my CSS like this:button:not(.no-hover):hover { background-color: blue; }
but that's probably quite bad for performance, and doesn't handle devices like the Chromebook Pixel (which has both a touchscreen and a mouse) correctly.I could add a
touch
class to thedocumentElement
and make my CSS like this:html:not(.touch) button:hover { background-color: blue; }
But that also doesn't work right on devices with both touch and a mouse.
我可以为每个按钮添加一个
no-hover
类ontouchend
,并使我的 CSS 像这样:button:not(.no-hover):hover { background-color: blue; }
但这可能对性能很不利,并且不能正确处理像 Chromebook Pixel(具有触摸屏和鼠标)这样的设备。我可以向 中添加一个
touch
类documentElement
并使我的 CSS 像这样:html:not(.touch) button:hover { background-color: blue; }
但这也不适用于同时具有触摸和鼠标的设备。
What I would prefer is removing the hover state ontouchend
. But it doesn't seem like that is possible. Focusing another element doesn't remove the hover state. Tapping another element manually does, but I can't seem to trigger that in JavaScript.
我更喜欢的是删除悬停状态ontouchend
。但这似乎不太可能。聚焦另一个元素不会删除悬停状态。手动点击另一个元素可以,但我似乎无法在 JavaScript 中触发它。
All the solutions I have found seem imperfect. Is there a perfect solution?
我发现的所有解决方案似乎都不完美。有没有完美的解决方案?
采纳答案by Sjoerd Visscher
You can remove the hover state by temporarily removing the link from the DOM. See http://testbug.handcraft.com/ipad.html
您可以通过从 DOM 中临时删除链接来删除悬停状态。见http://testbug.handcraft.com/ipad.html
In the CSS you have:
在 CSS 中,您有:
:hover {background:red;}
In the JS you have:
在 JS 你有:
function fix()
{
var el = this;
var par = el.parentNode;
var next = el.nextSibling;
par.removeChild(el);
setTimeout(function() {par.insertBefore(el, next);}, 0)
}
And then in your HTML you have:
然后在你的 HTML 中你有:
<a href="#" ontouchend="this.onclick=fix">test</a>
回答by cvrebert
Once CSS Media Queries Level 4is implemented, you'll be able to do this:
实现CSS Media Queries Level 4 后,您将能够执行以下操作:
@media (hover: hover) {
button:hover {
background-color: blue;
}
}
Or in English: "If the browser supports proper/true/real/non-emulated hovering (e.g. has a mouse-like primary input device), then apply this style when button
s are hovered over."
或用英语:“如果浏览器支持正确/真实/真实/非模拟悬停(例如,具有类似鼠标的主要输入设备),则button
在悬停 s时应用此样式。”
Since this part of Media Queries Level 4 has so far only been implemented in bleeding-edge Chrome, I wrote a polyfillto deal with this. Using it, you can transform the above futuristic CSS into:
由于 Media Queries Level 4 的这一部分到目前为止只在前沿的 Chrome 中实现,我写了一个 polyfill来解决这个问题。使用它,您可以将上述未来派 CSS 转换为:
html.my-true-hover button:hover {
background-color: blue;
}
(A variation on the .no-touch
technique) And then using some client-side JavaScript from the same polyfill that detects support for hovering, you can toggle the presence of the my-true-hover
class accordingly:
(该.no-touch
技术的一种变体)然后使用来自同一 polyfill 的一些客户端 JavaScript 来检测对悬停的支持,您可以相应地切换my-true-hover
类的存在:
$(document).on('mq4hsChange', function (e) {
$(document.documentElement).toggleClass('my-true-hover', e.trueHover);
});
回答by Tim Medora
This is a common problem with no perfect solution. Hover behavior is useful with a mouse and mostly detrimental with touch. Compounding the problem are devices which support touch and mouse (simultaneously, no less!) like the Chromebook Pixel and Surface.
这是一个普遍的问题,没有完美的解决方案。悬停行为对鼠标很有用,而对触摸则最不利。使问题更加复杂的是支持触摸和鼠标(同时,不少于!)的设备,如 Chromebook Pixel 和 Surface。
The cleanest solution I've found is to only enable hover behavior if the device isn't deemed to support touch input.
我发现的最干净的解决方案是仅在设备不支持触摸输入时启用悬停行为。
var isTouch = !!("ontouchstart" in window) || window.navigator.msMaxTouchPoints > 0;
if( !isTouch ){
// add class which defines hover behavior
}
Granted, you lose hover on devices which may support it. However, sometimes hover impacts more than the link itself, e.g. perhaps you want to show a menu when an element is hovered. This approach allows you to test for the existence of touch and perhaps conditionally attach a different event.
当然,您无法在可能支持它的设备上悬停。然而,有时悬停的影响不仅仅是链接本身,例如,您可能希望在悬停元素时显示菜单。这种方法允许您测试触摸是否存在,并可能有条件地附加不同的事件。
I've tested this on the iPhone, iPad, Chromebook Pixel, Surface, and a variety of Android devices. I can't guarantee that it will work when a generic USB touch input (such as a stylus) is added to the mix.
我已经在 iPhone、iPad、Chromebook Pixel、Surface 和各种 Android 设备上对此进行了测试。我不能保证将通用 USB 触摸输入(例如手写笔)添加到混合中时它会起作用。
回答by rachel
With Modernizryou can target your hovers specifically for no-touch devices:
使用Modernizr,您可以专门针对非触摸设备定位您的悬停:
(Note: this doesn't run on StackOverflow's snippet system, check the jsfiddleinstead)
(注意:这不会在 StackOverflow 的代码段系统上运行,请改为检查jsfiddle)
/* this one is sticky */
#regular:hover, #regular:active {
opacity: 0.5;
}
/* this one isn't */
html.no-touch #no-touch:hover, #no-touch:active {
opacity: 0.5;
}
Note that :active
doesn't need to be targeted with .no-touch
because it works as expected on both mobile and desktop.
请注意,:active
它不需要被定位,.no-touch
因为它在移动设备和桌面设备上都可以正常工作。
回答by coco puffs
From 4 ways to deal with sticky hover on mobile: Here's a way to dynamically add or remove a "can touch
" class to the document based on the current input type of the user. It works with hybrid devices as well where the user may be switching between touch and a mouse/trackpad:
从4 种方法来处理移动设备上的粘性悬停:这是一种can touch
根据用户的当前输入类型向文档动态添加或删除“ ”类的方法。它也适用于混合设备,用户可以在触摸和鼠标/触控板之间切换:
<script>
;(function(){
var isTouch = false //var to indicate current input type (is touch versus no touch)
var isTouchTimer
var curRootClass = '' //var indicating current document root class ("can-touch" or "")
function addtouchclass(e){
clearTimeout(isTouchTimer)
isTouch = true
if (curRootClass != 'can-touch'){ //add "can-touch' class if it's not already present
curRootClass = 'can-touch'
document.documentElement.classList.add(curRootClass)
}
isTouchTimer = setTimeout(function(){isTouch = false}, 500) //maintain "istouch" state for 500ms so removetouchclass doesn't get fired immediately following a touch event
}
function removetouchclass(e){
if (!isTouch && curRootClass == 'can-touch'){ //remove 'can-touch' class if not triggered by a touch event and class is present
isTouch = false
curRootClass = ''
document.documentElement.classList.remove('can-touch')
}
}
document.addEventListener('touchstart', addtouchclass, false) //this event only gets called when input type is touch
document.addEventListener('mouseover', removetouchclass, false) //this event gets called when input type is everything from touch to mouse/ trackpad
})();
</script>
回答by Verard Sloggett
$("#elementwithhover").click(function() {
// code that makes element or parent slide or otherwise move out from under mouse.
$(this).clone(true).insertAfter($(this));
$(this).remove();
});
回答by OrganicPanda
You can override the hover effect for devices that don't support hover. Like:
您可以覆盖不支持悬停的设备的悬停效果。喜欢:
.my-thing {
color: #BADA55;
}
.my-thing:hover {
color: hotpink;
}
@media (hover: none) {
.my-thing {
color: #BADA55;
}
}
Tested and verified on iOS 12
在 iOS 12 上测试和验证
Hat tip to https://stackoverflow.com/a/50285058/178959for pointing this out.
回答by Pere
I was going to post my own solution, but checking if someone already posted it, I found that @Rodney almost did it. However, he missed it one last crucial that made it uniseful, at least in my case. I mean, I too took the same .fakeHover
class addition / removing via mouseenter
and mouseleave
event detection, but that alone, per se, acts almost exactly like "genuine" :hover
. I mean: when you tap on a element in your table, it won't detect that you have "leaved" it - thus keeping the "fakehover" state.
我打算发布我自己的解决方案,但检查是否有人已经发布了它,我发现@Rodney 几乎做到了。然而,他错过了最后一个让它变得不合时宜的关键,至少在我的情况下是这样。我的意思是,我也通过相同的.fakeHover
类添加/删除mouseenter
和mouseleave
事件检测,但仅此而已,就其本身而言,其行为几乎与“正版”完全一样:hover
。我的意思是:当你点击表格中的一个元素时,它不会检测到你已经“离开”它——从而保持“fakehover”状态。
What I did was simply to listen on click
, too, so when I "tap" the button, I manually fire a mouseleave
.
我所做的也只是为了听click
,所以当我“点击”按钮时,我手动触发了一个mouseleave
.
Si this is my final code:
Si这是我的最终代码:
.fakeHover {
background-color: blue;
}
$(document).on('mouseenter', 'button.myButton',function(){
$(this).addClass('fakeHover');
});
$(document).on('mouseleave', 'button.myButton',function(){
$(this).removeClass('fakeHover');
});
$(document).on('button.myButton, 'click', function(){
$(this).mouseleave();
});
This way you keep your usual hover
functionality when using a mouse when simply "hovering" on your buttons. Well, almost all of it: the only drawback somehow is that, after clicking on the button with the mouse, it wont be in hover
state. Much like if you clicked and quickly took the pointer out of the button. But in my case I can live with that.
这样,您hover
只需将鼠标“悬停”在按钮上即可使用鼠标时保持通常的功能。好吧,几乎所有这些:不知何故,唯一的缺点是,在用鼠标单击按钮后,它不会处于hover
状态。就像您单击并快速将指针从按钮中取出一样。但就我而言,我可以忍受。
回答by Kevin Lee
This is what I have come up with so far after studying the rest of the answers. It should be able to support touch-only, mouse-only or hybrid users.
这是我在研究了其余答案后到目前为止得出的结论。它应该能够支持仅触摸、仅鼠标或混合用户。
Create a separate hover class for the hover effect. By default, add this hover class to our button.
为悬停效果创建一个单独的悬停类。默认情况下,将此悬停类添加到我们的按钮中。
We do not want to detect the presence of touch support and disable all hover effects from the very beginning. As mentioned by others, hybrid devices are gaining popularity; people may have touch support but want to use a mouse and vice versa. Therefore, only remove the hover class when the user actually touches the button.
我们不想从一开始就检测触摸支持的存在并禁用所有悬停效果。正如其他人所提到的,混合设备越来越受欢迎。人们可能有触摸支持但想要使用鼠标,反之亦然。因此,只有在用户实际触摸按钮时才删除悬停类。
The next problem is, what if the user wants to go back to using a mouse after touching the button? To solve that, we need to find an opportune moment to add back the hover class which we have removed.
下一个问题是,如果用户在触摸按钮后想重新使用鼠标怎么办?为了解决这个问题,我们需要找到一个合适的时机来添加我们已经删除的悬停类。
However, we cannot add it back immediately after removing it, because the hover state is still active. We may not want to destroy and recreate the entire button as well.
但是,我们不能在删除它后立即将其添加回来,因为悬停状态仍然处于活动状态。我们可能也不想销毁和重新创建整个按钮。
So, I thought of using a busy-waiting algorithm (using setInterval) to check the hover state. Once the hover state is deactivated, we can then add back the hover class and stop the busy-waiting, bringing us back to the original state where the user can use either mouse or touch.
所以,我想到了使用忙等待算法(使用 setInterval)来检查悬停状态。一旦悬停状态被停用,我们就可以重新添加悬停类并停止忙等待,使我们回到用户可以使用鼠标或触摸的原始状态。
I know busy-waiting isn't that great but I'm not sure if there is an appropriate event. I've considered to add it back in the mouseleave event, but it was not very robust. For example, when an alert pops up after the button is touched, the mouse position shifts but the mouseleave event is not triggered.
我知道忙碌的等待并不是那么好,但我不确定是否有合适的活动。我已经考虑在 mouseleave 事件中重新添加它,但它不是很健壮。例如,当触摸按钮后弹出警报时,鼠标位置移动但不会触发 mouseleave 事件。
var button = document.getElementById('myButton');
button.ontouchstart = function(e) {
console.log('ontouchstart');
$('.button').removeClass('button-hover');
startIntervalToResetHover();
};
button.onclick = function(e) {
console.log('onclick');
}
var intervalId;
function startIntervalToResetHover() {
// Clear the previous one, if any.
if (intervalId) {
clearInterval(intervalId);
}
intervalId = setInterval(function() {
// Stop if the hover class already exists.
if ($('.button').hasClass('button-hover')) {
clearInterval(intervalId);
intervalId = null;
return;
}
// Checking of hover state from
// http://stackoverflow.com/a/8981521/2669960.
var isHovered = !!$('.button').filter(function() {
return $(this).is(":hover");
}).length;
if (isHovered) {
console.log('Hover state is active');
} else {
console.log('Hover state is inactive');
$('.button').addClass('button-hover');
console.log('Added back the button-hover class');
clearInterval(intervalId);
intervalId = null;
}
}, 1000);
}
.button {
color: green;
border: none;
}
.button-hover:hover {
background: yellow;
border: none;
}
.button:active {
border: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='myButton' class='button button-hover'>Hello</button>
Edit: Another approach I tried is to call e.preventDefault()
within ontouchstart or ontouchend. It appears to stop the hover effect when the button is touched, but it also stops the button click animation and prevents the onclick function from being called when the button is touched, so you have to call those manually in the ontouchstart or ontouchend handler. Not a very clean solution.
编辑:我尝试的另一种方法是e.preventDefault()
在 ontouchstart 或 ontouchend 内调用。它似乎在触摸按钮时停止悬停效果,但它也会停止按钮单击动画并防止在触摸按钮时调用 onclick 函数,因此您必须在 ontouchstart 或 ontouchend 处理程序中手动调用它们。不是一个非常干净的解决方案。
回答by Micha? Reszka
I have nice solution that i would like to share. First you need to detect if user is on mobile like this:
我有很好的解决方案,我想分享。首先,您需要检测用户是否像这样使用移动设备:
var touchDevice = /ipad|iphone|android|windows phone|blackberry/i.test(navigator.userAgent.toLowerCase());
Then just add:
然后只需添加:
if (!touchDevice) {
$(".navbar-ul").addClass("hoverable");
}
And in CSS:
在 CSS 中:
.navbar-ul.hoverable li a:hover {
color: #fff;
}