• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

h5移动端新增事件

武飞扬头像
song854601134
帮助1

为什么touch事件中要阻止浏览器的默认行为

那肯定是在能支持触屏的设备上运行,比如手机,手机上你滑动的时候他本身就有个默认的滚屏,因此如果你要操作操作你个dom元素,用touchmove事件,这其实也是滑动。原本你的效果是要在dom上滑动然后产生相应的效果,这个时候你肯定不希望手机上的浏览器屏幕滚动,所以你应该把它默认的滚动给禁止了。这样才会有好的用户体验。

touchmove 默认会有滚动效果吗!??

移动端,同一页面不同层的划动操作会造成相互影响,需要额外处理

如:页面层和弹出层(如picker选择划动)都有划动,同一个页面的不同层划动操作,造成相互影响,需要启用与禁用touchmove来处理.
addEventListener()和removeEventListener()用于处理指定和删除事件处理程序操作,所有DOM节点中都包含这两种方法,
并动接受3个参数:
element.addEventListener(type, listener, useCapture)
element: 要绑定函数的对象;
type: 事件名称, 'click', 'blur',不能加'on';
listener: 绑定的事件函数,不要跟括号;
useCapture: 布尔值,表示该事件的响应顺序,
true -> 表示在捕获阶段调用事件处理程序, false -> 表示在冒泡阶段调用事件处理程序
                        使用:
var handler = function(e){
e.preventDefault();
}
document.addEventListener('touchmove', handler, false);//阻止默认事件
document.removeEventListener('touchmove', handler, false);//打开默认事件
注意: 添加和移除的函数对象应该是同一个,要不然移除不起作用,即listener不能直接用匿名函数function(e){e.preventDefault();}

在vue中阻止touchmove的滚动效果 vue中提供 @touchmove.prevent 方法也可以完美解决这个问题。

vue同时还提供了其他的修饰符:

阻止单击事件继续传播 :

<a v-on:click.stop="doThis"></a> 

提交事件不再重载页面 :

<form v-on:submit.prevent="onSubmit"></form> 

修饰符可以串联 :

<a v-on:click.stop.prevent="doThat"></a> 

只有修饰符 :

<form v-on:submit.prevent></form> 

添加事件监听器时使用事件捕获模式, 即元素自身触发的事件先在此处理,然后才交由内部元素进行处理 :

<div v-on:click.capture="doThis">...</div> 

只当在 event.target 是当前元素自身时触发处理函数 ,即事件不是从内部元素触发的 :

<div v-on:click.self="doThat">...</div>

移动端 js touch事件

HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享。今天为大家介绍的事件主要是触摸事件:touchstart、touchmove和touchend。

一开始触摸事件touchstart、touchmove和touchend是iOS版Safari浏览器为了向开发人员传达一些信息新添加的事件。因为ios设备既没有鼠标也没有键盘,所以在为移动Safari浏览器开发交互性网页的时候,PC端的鼠标和键盘事件是不够用的。

在iPhone 3Gs发布的时候,其自带的移动Safari浏览器就提供了一些与触摸(touch)操作相关的新事件。随后,Android上的浏览器也实现了相同的事件。触摸事件(touch)会在用户手指放在屏幕上面的时候、在屏幕上滑动的时候或者是从屏幕上移开的时候出发。下面具体说明:

常用事件

  • touchstart :当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。
  • touchmove :当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动。
  • touchend :当手指从屏幕上离开的时候触发。
  • touchcancel事件:当系统停止跟踪触摸的时候触发。关于这个事件的确切出发时间,文档中并没有具体说明,咱们只能去猜测了。
  • touchenter :移动的手指进入一个dom元素。
  • touchleave :移动的手指离开一个dom元素。
上面的这些事件都会冒泡,也都可以取消。虽然这些触摸事件没有在DOM规范中定义,
但是它们却是以兼容DOM的方式实现的。
所以,每个触摸事件的event对象都提供了在鼠标实践中常见的属性:bubbles(起泡事件的类型)、cancelable(是否用 preventDefault() 方法可以取消与事件关联的默认动作)、
clientX(返回当事件被触发时,鼠标指针的水平坐标)、
clientY(返回当事件触发时,鼠标指针的垂直坐标)、
screenX(当某个事件被触发时,鼠标指针的水平坐标)
和screenY(返回当某个事件被触发时,鼠标指针的垂直坐标)。除了常见的DOM属性,
触摸事件还包含下面三个用于跟踪触摸的属性。```
**事件属性**:
altKey : 该属性返回一个布尔值,表示在指定的事件发生时,Alt 键是否处于按下状态, event.altKey=true|false|1|0

type : 触摸时触发的事件类型,比如touchstart

每个触摸事件都包括了三个触摸属性列表:

1. touches:当前位于屏幕上的所有手指触摸点的一个列表。

2. targetTouches:当前元素对象上所有触摸点的列表。

3. changedTouches:涉及当前事件的触摸点的列表。

它们都是一个数组,每个元素代表一个触摸点。

每个触摸点对应的Touch都有三对重要的属性,clientX/clientY、pageX/pageY、screenX/screenY。

其中screenX/screenY代表事件发生的位置对于屏幕的偏移量,clientX/clienYt和pageX/pageY都代表事件发生位置对应对象的偏移量,不过区别是clientX/clientY不包括对象滚动而隐藏的偏移量,而pageX/pageY包括对象滚动而隐藏的偏移量。移开屏幕的那个触摸点,只会包含在changedTouches列表中,而不会包含在touches 和targetTouches 列表中, 所以changedTouches在项目当中会比较常用。

示例:

<body onload="start();">
<style type="text/css">
#dom {
  width:500px;
  height:500px;
  background:black;
}
</style>
<div id="dom"></div>
<script type="text/javascript">
function onTouchStart(e){
    console.log(e);
}
function start(){
    var dom = document.getElementById('dom');
    dom.addEventListener('touchstart', onTouchStart, false);
}
</script>
</body>

控制台输出如下:
学新通

用一个手指接触屏幕,触发事件,此时这三个属性有相同的值。

用第二个手指接触屏幕,此时,touches有两个元素,每个手指触摸点为一个值。当两个手指触摸相同元素时,
targetTouches和touches的值相同,否则targetTouches 只有一个值。changedTouches此时只有一个值,
为第二个手指的触摸点,因为第二个手指是引发事件的原因

用两个手指同时接触屏幕,此时changedTouches有两个值,每一个手指的触摸点都有一个值

手指滑动时,三个值都会发生变化

一个手指离开屏幕,touches和targetTouches中对应的元素会同时移除,而changedTouches仍然会存在元素。

手指都离开屏幕之后,touches和targetTouches中将不会再有值,changedTouches还会有一个值,
此值为最后一个离开屏幕的手指的接触点。


触摸事件跟鼠标事件的触发先后顺序:

Touchstart > toucheend > mousemove > mousedown > mouseup > click

很多情况下触摸事件跟鼠标事件会同时触发(目的是为了让没有对触摸设备优化的代码仍然可以在触摸设备上正常工作),如果使用了触摸事件,可以调用event.preventDefault()来阻止鼠标事件被触发。而手指在屏幕上移动touchmove则不会触发鼠标事件和单击事件,在touchmove事件中加入preventDefault, 可以禁止浏览器滚动屏幕,也不会影响单击事件的触发。
学新通

以上事件,都系统内置的,可以直接使用,通过这些内置事件,可以组合成很多非原生的多点触摸手势touch手势。

事件的passive是什么鬼

很久以前,addEventListener() 的参数约定是这样的:

很久以前,addEventListener() 的参数约定是这样的:

addEventListener(type, listener, useCapture)

后来,最后一个参数,也就是控制监听器是在捕获阶段执行还是在冒泡阶段执行的 useCapture 参数,变成了可选参数(传 true 的情况太少了),成了:

addEventListener(type, listener[, useCapture ])

去年年底,DOM 规范做了修订:addEventListener() 的第三个参数可以是个对象值了,也就是说第三个参数现在可以是两种类型的值了:

addEventListener(type, listener[, useCapture ])
addEventListener(type, listener[, options ])

这个修订是为了扩展新的选项,从而自定义更多的行为,目前规范中 options 对象可用的属性有三个:

addEventListener(type, listener, {
    capture: false,
    passive: false,
    once: false
})

三个属性都是布尔类型的开关,默认值都为 false。其中 capture 属性等价于以前的 useCapture 参数;once 属性就是表明该监听器是一次性的,执行一次后就被自动 removeEventListener 掉,还没有浏览器实现它;passive 属性是本文的主角,Firefox 和 Chrome 已经实现,先看个 Chrome 官方的视频介绍(单击播放):

很多移动端的页面都会监听 touchstart 等 touch 事件,像这样:

document.addEventListener("touchstart", function(e){
    ... // 浏览器不知道这里会不会有 e.preventDefault()
})
由于 touchstart 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止,那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。视频里也说了,即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。

视频里还说了,有 80% 的滚动事件监听器是不会阻止默认行为的,也就是说大部分情况下,浏览器是白等了。所以,passive 监听器诞生了,passive 的意思是“顺从的”,表示它不会对事件的默认行为说 no,浏览器知道了一个监听器是 passive 的,它就可以在两个线程里同时执行监听器中的 JavaScript 代码和浏览器的默认行为了。

下面是在 Chrome for Android 上滚动 cnn.com 页面的对比视频,右边在注册 touchstart 事件时添加了 {passive: true} 选项,左边没有,可以看到,右边的顺畅多了。

假如在一个 passive 的监听器里执行了 preventDefault() 会怎么样?

假如有人不小心在 passive 的监听器里调用了 preventDefault(),也无妨,因为 preventDefault() 不会产生任何效果。这里我用自定义事件演示一下这种情况:

let event = new Event("foo", {  // 创建一个 type 为 foo 的事件对象,可以被阻止默认行为 
  "cancelable": true
})

document.addEventListener("foo", function(event) { // 在 document 上绑定 foo 事件的监听函数
  console.log(event.defaultPrevented) // false
  event.preventDefault()
  console.log(event.defaultPrevented) // 还是 false,preventDefault() 无效
}, {
  passive: true
})
 
document.dispatchEvent(event) // 派发自定义事件

同时,浏览器的开发者工具也会发出警告:

Chrome 下:
学新通
Firefox 下:
学新通

开发者工具的支持

除了上面在 passive 的监听器里调用 preventDefault() 会发出警告外,Chrome 的开发者工具还会:

  1. 发现耗时超过 100 毫秒的非 passive 的监听器,警告你加上 {passive: true}:
    学新通
  2. 给监听器对象增加 passive 属性,监听器对象在普通页面中是获取不到的,可以在 Event Listeners 面板中和通过调用 getEventListeners() Command Line API 获取到:
    学新通
    Firefox 的开发者工具目前还没有这些。

现在该如何调用 removeEventListener?

见百度网盘收录讲解视频

以前,在第三个参数是布尔值的时候,addEventListener(“foo”, listener, true) 添加的监听器,必须用 removeEventListener(“foo”, listener, true) 才能删除掉。因为这个监听器也有可能还注册在了冒泡阶段,那样的话,同一个监听器实际上对应着两个监听器对象(通过 getEventListeners() 可看到)。

那现在 addEventListener(“foo”, listener, {passive: true}) 添加的监听器该如何删除呢?答案是 removeEventListener(“foo”, listener) 就可以了,passive 可以省略,原因是:在浏览器内部,用来存储监听器的 map 的 key 是由事件类型,监听器函数,是否捕获这三者组成的passive 和 once 不在其中,理由显而易见,一个监听器同时是 passive 和非 passive(以及同时是 once 和非 once)是说不通的,如果你添加了两者,那么后添加的不算,浏览器会认为添加过了:

addEventListener("foo", listener, {passive: true})
addEventListener("foo", listener, {passive: false}) // 这句不算

addEventListener("bar", listener, {passive: false})
addEventListener("bar", listener, {passive: true})  // 这句不算

所以说在 removeEventListener 的时候永远不需写 passive 和 once,但 capture 可能要:

addEventListener("foo", listener, {capture: true})
removeEventListener("foo", listener, {capture: true}) // {capture: true} 必须加,当然 {capture: true} 换成 true 也可以

passive 不能保证什么

passive 监听器能保证的只有一点,那就是调用 preventDefault() 无效,至于浏览器对默认行为卡顿的优化,那是浏览器的事情,是在规范要求之外的。鉴于这个新特性本来就是为解决滚动和触摸事件的卡顿而发明的,目前 Chrome 和 Firefox 支持优化的事件类型也仅限这类事件,比如 touchstart,touchmove,wheel 等事件,具体的事件列表我无法提供,也许得研读源码才行。

但我可以列举几个浏览器不优化的事件类型,还附带 demo:

除了这三种事件类型外,所有 Cancelable 为 true 的事件类型理论上都是可以有这种优化的, 可以看看这个 UI 事件类型列表,还有这个触摸事件类型列表,注意 Cancelable 列。

我咨询了 Chrome 工程师,有没有优化滚动和触摸事件类型之外事件类型的计划,答复是目前没有:

学新通

Firefox 的 APZ 优化

在 passive 规范之前,Firefox 就已经有了自己对滚动触摸行为卡顿问题的优化,其中有个关键做法是,不尊重 preventDefault():如果在一定时间之内没有调用 preventDefault(),那 Firefox 就假定你不会阻止默认滚动了,比如执行下面这句后,页面会无法滚动:

addEventListener("wheel", function (e) {
    e.preventDefault()
})

但执行这句后照样能滚动:

addEventListener("wheel", function (e) {
    sleep(300)
    e.preventDefault() // 这句在 Firefox 中无效
})

这篇博客讲了 APZ 优化:Smoother scrolling in Firefox 46 with APZ

特性检测

下面是从 Modernizr 里复制的检测脚本:

var supportsPassiveOption = false;
try {
  var opts = Object.defineProperty({}, 'passive', {
    get: function() {
      supportsPassiveOption = true;
    }
  });
  window.addEventListener('test', null, opts);
} catch (e) {}

可以这么用:

if (supportsPassiveOption) {
    document.addEventListener("foo", listener, {passive: true}) // 旧浏览器里第三参数会被自动转成 true,不是我们想要的
}
else {
    document.addEventListener("foo", listener)
}

要 passive 都得 passive

对于在同一个 DOM 对象身上添加的同一类型事件的监听器,只要有一个不是 passive 的,那浏览器就无法优化。

各大框架目前还没一个使用该特性

https://github.com/facebook/react/issues/6436

https://github.com/angular/angular/issues/8866

https://github.com/emberjs/ember.js/issues/12783

https://github.com/Polymer/polymer/issues/3604

https://github.com/jquery/jquery/issues/2871

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfifkkg
系列文章
更多 icon
同类精品
更多 icon
继续加载