由于触摸事件上的类别切换/高度更改而错误放置了可点击区域

cha*_*enu 6 html javascript css touch touch-event

如果您切换放置在上方的元素的高度,则触摸设备上有关于链接的可点击区域的奇怪行为.如果您运行以下代码段(例如,将其保存在本地并使用chrome来模拟触摸事件),您会注意到#mylink在某些情况下您没有点击红色链接区域的哈希值会被添加到网址中.有时你点击红色区域,它不会被添加.

例如

  • 如果单击上部灰色区域(前200px),灰色区域将切换高度.=>好的.
  • 如果单击增强的较低灰色区域(200px - 400px),#mylink则会将其添加到URL中,就像您单击红色链接区域一样.=>为什么?
  • 如果单击上部红色区域(前200px,灰色区域最小化),#mylink则不会添加到URL.=>为什么?
  • 如果单击较低的红色区域(最后200px,灰色区域增强),#mylink则不会添加到URL.=>为什么?

<html>
<head>
	<style type="text/css">
		.test {
			background: grey;
			height: 200px;
		}

		.test.is-visible {
			height: 400px;
		}

		.link {
			height: 600px;
			display: block;
			background: red;
		}
	</style>
	<script type="text/javascript">
	  (function() {
	    window.addEventListener( 'touchstart' , function() {
	    	document.getElementsByClassName("test")[0].classList.toggle('is-visible');
	    });	
		})();
	</script>
</head>

<body>
	<div class="test">
		Click the grey area. Element enhances and minimizes itself. Now click on the lower half of the enhanced grey area (if grey = enhanced) or on the white area up to 200px below of the red area (if grey = minimized). #mylink is added to the url as if you had clicked on the red area. Why?
	</div>
	<a href="#mylink" class="link">The red area is the "normal" click area.</a>
</body>

</html>
Run Code Online (Sandbox Code Playgroud)

我在iOS和Android上尝试了这一功能,并在Chrome和Firefox中模拟了触摸事件.他们都表现得一样.

如果我听click的,而不是touchstarttouchend事件,它将按预期工作.

造成这种差异的原因是什么?如果我听触摸事件而不是点击事件,为什么链接区域不同/错位?

cha*_*enu 2

长话短说

浏览器在 的位置上发生触摸事件后执行单击事件touchend,无论内容是否由于分配给触摸事件的功能而发生更改。


在深入研究了触摸事件规范之后,我想我可以回答我的问题:

第 8 节描述了触摸事件与鼠标事件和单击的交互。它说:

用户代理可以响应相同的用户输入来调度触摸事件和(...)鼠标事件。

如果用户代理将一系列触摸事件解释为点击手势,那么它应该在相应触摸输入的touchend 事件的位置调度 mousemove、mousedown、mouseup 和 click 事件(按此顺序) 。

如果文档的内容在触摸事件的处理期间发生了改变,则用户代理可以将鼠标事件分派到与触摸事件不同的目标。

最重要的是

元素的激活(例如,在某些实现中,点击)通常会产生以下事件序列(尽管这可能略有不同,具体取决于特定的用户代理行为):

1.)触摸启动

2.) 零个或多个 touchmove 事件,具体取决于手指的移动

3.)触摸端

4.) mousemove(为了与遗留的鼠标特定代码兼容)

5.) 鼠标按下

6.) 鼠标松开

7.)单击

因此,总结上面的示例,这意味着:

1.)touchstart或被touchend触发。

2.) 首先处理自定义函数,这会切换类并更改元素的高度/位置。

3.) 之后,click事件在事件发生的同一点执行touch,但现在覆盖不同的目标。

该规范也提供了一种防止这种情况的方法:

如果 touchstart、touchmove 或 touchend 被取消,则用户代理不应分派任何鼠标事件,因为该事件可能是被阻止的触摸事件的结果。

所以我想“修复”此行为的最简单方法是使用“preventDefault()”阻止该事件,然后手动单击事件的目标并最后切换类:

     window.addEventListener( 'touchstart' , function(event) {
        event.preventDefault();

        event.target.click();

        document.getElementsByClassName("test")[0].classList.toggle('is-visible');
    }); 
Run Code Online (Sandbox Code Playgroud)

Chromium bug tracker 中的一个声明也证明了这一点。