由于 class 切换/触摸事件高度变化导致可点击区域错位
Misplaced clickable area due to class toggle / height change on touch event
如果您切换放置在上方的元素的高度,link 的可点击区域在触摸设备上会出现奇怪的行为。如果您 运行 以下代码段(例如,将其保存在本地并使用 chrome 来模拟触摸事件),您会注意到散列 #mylink
被添加到 url 中在您没有点击红色 link 区域的情况下。而且有的时候你点击了红色区域,是不会添加的。
例如
- 如果单击上方的灰色区域(前 200px),灰色区域会切换高度。 => 好的。
- 如果单击增强的下部灰色区域 (200px - 400px),
#mylink
将添加到 url,就像您单击红色 link 区域一样。 => 为什么?
- 如果单击上方的红色区域(前 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>
我在 iOS、Android 上进行了尝试,并在 Chrome 和 Firefox 中模拟了触摸事件。他们的行为都一样。
如果我收听 click
而不是 touchstart
或 touchend
事件,它会按预期工作。
造成这种差异的原因是什么?如果我监听触摸事件而不是点击事件,为什么 link 区域不同/错位?
这是增强灰色区域并触摸下半部分时发生的情况:
- 当你触摸灰色区域的下半部分时,你的
touchstart
事件被触发
- 您的函数运行并切换(删除)灰色元素
is-visible
class
- 灰色元素的高度变小了
- 红色元素向上移动
200px
- 您的触摸没有移动,但页面上的元素移动了 - 您现在正在触摸红色元素
- 当您释放触摸时,红色元素上会触发
touchend
事件,该事件决定 link 是否被触摸
当您点击红色元素下方的区域 200px
时会发生同样的事情:
- 当您触摸红色元素下方的区域时,会触发您的
touchstart
事件
- 您的函数运行并切换(添加)灰色元素
is-visible
class
- 灰色元素的高度变大了
- 红色元素向下移动
200px
- 您的触摸没有移动,但页面上的元素移动了 - 您现在正在触摸红色元素
- 当您释放触摸时,红色元素上会触发
touchend
事件,该事件决定 link 是否被触摸
TL;DR
浏览器在 touchend
位置发生触摸事件后执行点击事件,无论内容是否因分配给触摸事件的功能而改变。
通过 Touch Events specification 挖掘后,我想我可以回答我的问题:
Section 8描述了Touch Events与Mouse Events和click的交互。它说:
The user agent may dispatch both touch events and (...) mouse events in response to the same user input.
和
If the user agent interprets a sequence of touch events as a tap gesture, then it should dispatch mousemove, mousedown, mouseup, and click events (in that order) at the location of the touchend event for the corresponding touch input.
If the contents of the document have changed during processing of the touch events, then the user agent may dispatch the mouse events to a different target than the touch events.
最重要的
The activation of an element (e.g., in some implementations, a tap) would typically produce the following event sequence (though this may vary slightly, depending on specific user agent behavior):
1.) touchstart
2.) Zero or more touchmove events, depending on movement of the finger
3.) touchend
4.) mousemove (for compatibility with legacy mouse-specific code)
5.) mousedown
6.) mouseup
7.) click
所以总结我们上面的例子,这意味着:
1.) touchstart
或touchend
被触发。
2.) 首先处理自定义函数,它切换 class 并更改元素的高度/位置。
3.) 之后,click
事件在 touch
事件发生的同一点执行,但现在覆盖了不同的目标。
规范也提供了一种防止这种情况的方法:
If touchstart, touchmove, or touchend are canceled, the user agent should not dispatch any mouse event that would be a consequential result of the prevented touch event.
所以我想“修复”此行为的最简单方法是使用 `preventDefault()' 阻止该事件,然后手动单击该事件的目标并最后切换 class:
window.addEventListener( 'touchstart' , function(event) {
event.preventDefault();
event.target.click();
document.getElementsByClassName("test")[0].classList.toggle('is-visible');
});
Chromium 错误跟踪器中有一个 statement 也证明了这一点。
如果您切换放置在上方的元素的高度,link 的可点击区域在触摸设备上会出现奇怪的行为。如果您 运行 以下代码段(例如,将其保存在本地并使用 chrome 来模拟触摸事件),您会注意到散列 #mylink
被添加到 url 中在您没有点击红色 link 区域的情况下。而且有的时候你点击了红色区域,是不会添加的。
例如
- 如果单击上方的灰色区域(前 200px),灰色区域会切换高度。 => 好的。
- 如果单击增强的下部灰色区域 (200px - 400px),
#mylink
将添加到 url,就像您单击红色 link 区域一样。 => 为什么? - 如果单击上方的红色区域(前 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>
我在 iOS、Android 上进行了尝试,并在 Chrome 和 Firefox 中模拟了触摸事件。他们的行为都一样。
如果我收听 click
而不是 touchstart
或 touchend
事件,它会按预期工作。
造成这种差异的原因是什么?如果我监听触摸事件而不是点击事件,为什么 link 区域不同/错位?
这是增强灰色区域并触摸下半部分时发生的情况:
- 当你触摸灰色区域的下半部分时,你的
touchstart
事件被触发 - 您的函数运行并切换(删除)灰色元素
is-visible
class - 灰色元素的高度变小了
- 红色元素向上移动
200px
- 您的触摸没有移动,但页面上的元素移动了 - 您现在正在触摸红色元素
- 当您释放触摸时,红色元素上会触发
touchend
事件,该事件决定 link 是否被触摸
当您点击红色元素下方的区域 200px
时会发生同样的事情:
- 当您触摸红色元素下方的区域时,会触发您的
touchstart
事件 - 您的函数运行并切换(添加)灰色元素
is-visible
class - 灰色元素的高度变大了
- 红色元素向下移动
200px
- 您的触摸没有移动,但页面上的元素移动了 - 您现在正在触摸红色元素
- 当您释放触摸时,红色元素上会触发
touchend
事件,该事件决定 link 是否被触摸
TL;DR
浏览器在 touchend
位置发生触摸事件后执行点击事件,无论内容是否因分配给触摸事件的功能而改变。
通过 Touch Events specification 挖掘后,我想我可以回答我的问题:
Section 8描述了Touch Events与Mouse Events和click的交互。它说:
The user agent may dispatch both touch events and (...) mouse events in response to the same user input.
和
If the user agent interprets a sequence of touch events as a tap gesture, then it should dispatch mousemove, mousedown, mouseup, and click events (in that order) at the location of the touchend event for the corresponding touch input.
If the contents of the document have changed during processing of the touch events, then the user agent may dispatch the mouse events to a different target than the touch events.
最重要的
The activation of an element (e.g., in some implementations, a tap) would typically produce the following event sequence (though this may vary slightly, depending on specific user agent behavior):
1.) touchstart
2.) Zero or more touchmove events, depending on movement of the finger
3.) touchend
4.) mousemove (for compatibility with legacy mouse-specific code)
5.) mousedown
6.) mouseup
7.) click
所以总结我们上面的例子,这意味着:
1.) touchstart
或touchend
被触发。
2.) 首先处理自定义函数,它切换 class 并更改元素的高度/位置。
3.) 之后,click
事件在 touch
事件发生的同一点执行,但现在覆盖了不同的目标。
规范也提供了一种防止这种情况的方法:
If touchstart, touchmove, or touchend are canceled, the user agent should not dispatch any mouse event that would be a consequential result of the prevented touch event.
所以我想“修复”此行为的最简单方法是使用 `preventDefault()' 阻止该事件,然后手动单击该事件的目标并最后切换 class:
window.addEventListener( 'touchstart' , function(event) {
event.preventDefault();
event.target.click();
document.getElementsByClassName("test")[0].classList.toggle('is-visible');
});
Chromium 错误跟踪器中有一个 statement 也证明了这一点。