这里会结合 click 对上面的事件进行讨论, touch 发生在 click 之前
先上段代码, 直观感受一下
- <!DOCTYPE html>
- <HTML>
- <head>
- <style type="text/CSS">
- #level0 { /* width: 500px; height: 500px; */ } #level1-0 { background:
- red; width: 500px; height: 500px; } #level1-1 { background: green; width:
- 500px; height: 500px; }
- </style>
- </head>
- <body>
- <div id="level0">
- <div id="level1-0">
- </div>
- <div id="level1-1">
- </div>
- </div>
- </body>
- <script type="text/javascript">
- var level10 = document.getElementById("level1-0");
- level10.addEventListener('touchstart',
- function(e) {
- console.log(1);
- });
- level10.addEventListener('touchmove',
- function(e) {
- console.log(2);
- });
- level10.addEventListener('touchend',
- function(e) {
- console.log(3);
- });
- level10.onclick = function() {
- console.log(5);
- }
- document.body.onclick = function() {
- console.log('6');
- }
- </script>
- </HTML>
在红色区域点击会出现什么效果呢? 出现的是 1 3 5 6, 奇怪了 touchmove 为何不执行, 因为我们并没有移动, 也就是说, 必须触碰到屏幕上面, 而且发生了移动动作, touchmove 才执行, 现在我们触碰到, 而且手指稍微动一下, 发现输出的效果是, 1 2(+) 3, 其中 touchmove 可能触发多次, 又奇怪了, click 为何不执行, 因为 click 执行的条件是 点击, 而且不移动 所以一般情况下, 我们可以理解成 touchmove 和 click 是相斥的.
我们知道, 当一个用户在点击屏幕的时候, 系统会触发 touch 事件和 click 事件, touch 事件优先处理, touch 事件经过 捕获, 处理, 冒泡 一系列流程处理完成后, 才回去触发 click 事件
既然 touch 事件和 click 事件有了优先级别, 那么能不能在 touch 阶段取消掉系统触发的 click 事件呢? 当然是可以的, 浏览器提供了这样的能力. 在 touch 事件里面, 调用 e.preventDefault() 就可以阻止本次点击系统触发的 click 事件, 即本次相关的 click 都不会执行
把上面代码稍微加一点
- level10.addEventListener('touchstart', function(e) {
- console.log(1);
- e.preventDefault();
- });
点击的时候 发现 只有 1 3, 说明 click 被阻止了, 当然在 touchend 里面加效果也一样, 所以 在 touch 事件里面加 e.preventDefault 可以取消系统产生的 click 事件, 当然不会阻止后面的 touch 事件.
用个具体的例子看看 如何解决点透问题
产生点透问题的原因, 可以先看看代码吧
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <style type="text/css">
- #level0 { /* width: 500px; height: 500px; */ position: relative; } #level1-0
- { position: absolute; z-index: 1; background: red; width: 500px; height:
- 500px; } #level1-1 { background: green; width: 500px; height: 500px; }
- </style>
- </head>
- <body>
- <div id="level0">
- <div id="level1-0">
- </div>
- <div id="level1-1">
- </div>
- </div>
- </body>
- <script type="text/javascript">
- var level10 = document.getElementById("level1-0");
- var level11 = document.getElementById("level1-1");
- level10.addEventListener('touchstart',
- function(e) {
- level10.style.display = 'none';
- });
- level11.onclick = function() {
- console.log('level11 莫名被点击了');
- } < /script
- </HTML >
本来是 level1-0 和 level1-1 是兄弟节点, 即他们之间不会发生什么 事件传递, 目前 level1-0 相当于一个覆盖层, 覆盖在 level1-1 上面, 按理说点击 level1-0 的时候, level1-0 会阻挡所有的事件, 事件不会传递给 level1-1, 当点击 level1-0 的时候, 实际上 level1-1 也发生了点击事件, 即上面的输出结果为 level1-0 消失, 输出
level11 莫名被点击了
, 这就是点透
点透发生的条件:
A 和 B 不是后代继承关系 (如果是后代继承关系的话, 就直接是冒泡子类的话题了)
A 发生 touch, A touch 后立即消失, B 事件绑定 click
A z-index 大于 B, 即 A 显示在 B 浮层之上
点透发生的理由:
当手指触摸到屏幕的时候, 系统生成两个事件, 一个是 touch 一个是 click,touch 先执行, touch 执行完成后, A 从文档树上面消失了, 而且由于移动端 click 还有延迟 200-300ms 的关系, 当系统要触发 click 的时候, 发现在用户点击的位置上面, 目前离用户最近的元素是 B, 所以就直接把 click 事件作用在 B 元素上面了
.
那如何才能解决点透问题呢? 还记得我之前说过么, 系统提供了先触发的 touch 事件去取消系统生成的 click 事件, 所以只要在 touch 事件的某个处理函数中 执行 e.preverDefault 即可, 一般我们在 touchend 中执行
在上面代码中, 加上这句就完美解决了
- level10.addEventListener('touchend', function(e) {
- e.preventDefault();
- });
当然点透问题, 还有其他的解决方法, 关键是 要么是需求本次系统生成的 click 事件, 要么是当系统触发 click 的时候, 当前的触发 touch 的那个 dom 节点还存在. 比如将其一延迟 3s 在关闭
- setTimeout(() => {
- level10.style.display = 'none';
- }, 300);
来源: http://www.bubuko.com/infodetail-2923591.html