JS事件的那些事

之前已经有博客介绍了js的事件,下面的作为补充。

为何移动端点击会有0.3s的延迟

移动端设备会有这样的现象,就是移动端页面对于触摸事件会有300ms的延迟,据研究表明,当延迟超过100ms时,用户就能感受到明显的卡顿,当延迟达到300ms,用户更能感受到界面响应速度的缓慢。

其实这应该追溯到手机开始支持双击缩放的时候了,ios上的safari为了能将pc端的大网页较好的展示在手机端上,开始使用双击缩放的方案。通过双击缩放,我们能够看清原本因为尺寸太小而看不清楚的字体,图片等。虽然这为我们带来了便利,但在另一方面却造成了困扰,要支持这个功能,浏览器就必须判断用户是否在极短的事件内双击,就算你只是想点击一个按钮,浏览器也会按照惯例等待300ms的时间来确定用户到底是不是要缩放页面,这也就是延迟的由来。

解决方法

使用fastclick.js

fastclick的使用方法简单,在window load事件后,在body上调用fastClick.attach()即可

1
2
3
window.addEventListener(function(){
FastClick.attach(document.body);
})

当 FastClick 检测到当前页面使用meta设置了user-scalable=no或者 touch-action 属性的解决方案时,会静默退出.

jQuery和zepto.js则使用tap事件取代click事件

tap事件的大致思路:

  • 在touchstart/touchend时记录时间,手指位置
  • 在touchend时进行比较,确认是否为同一位置(或允许较小的位移)
  • 判断时间间隔(一般为200ms)
  • 过程未曾触发touchmove,即认为触发移动端的’click’,成为’tap’

meta viewport

meta viewport中指定页面不可缩放,则click时不存在300ms延迟

mouseenter/mouseleave 和 mouseover/mouseout

区别:

  • mouseover,mouseout可冒泡,mouseenter,mouseleave不可冒泡
  • mouseenter只作用于目标元素,进入子元素返回不能再触发(进入只触发一次)
  • mouseover作用于目标元素及后代元素,进入子元素返回可再触发父元素的事件(mouseout)(可多次触发)
  • mouseleave与mouseenter类似,各自只触发自己的事件
  • mouseout与mouseover类似,进入子元素可触发父元素的事件(mouseover)

mouseover/mouseout看来,它的事件都与后代元素有关进入子元素从子元素移出会触发父元素相应的mouseoutmouseover,且子元素事件会冒泡;而在mouseenter/mouseleave中,一旦进入了元素,无论是子元素或者父元素,都看作单独的个体,各自触发事件且不会冒泡,从父元素进入子元素和从子元素移出到父元素都不会触发父元素的事件。

总结:这两组事件的区别总结起来就是:是否冒泡和子元素的移入移出是否会触发父元素的事件。牢记这两点就ok了!

clientX/clientY 和 pageX/pageY

当我们通过鼠标触发页面的鼠标事件时,就会产生一个事件对象e并传给我们绑定的事件函数,里面包含很多的内容。

通常鼠标操作的话,主要是要获取鼠标当前的坐标值,并利用坐标值经过一系列的计算来完成我们的目的,例如:音乐播放器的进度条的拉拽,页面上元素的拖拽以及边界的判断等等。

而事件对象e中就提供了很多有用的信息:最常用的还是clientX/clientY 和 pageX/pageY这两组属性。

client表示的是页面的可视区域(即当前浏览器窗口),而page表示的则是整个页面(包括超出窗口宽高的部分)

这样就很明了了,在没发生滚动的情况下,两组属性随便你选,但是一旦发生滚动,还是需要选择page开头的属性,才能获得正确的数据。

兼容性:IE不支持,需要用event.y和event.y获取

除了上面两组属性外,通过鼠标触发的事件e中还有screenX和screenY,不过不是很常用,就不解释了。

DOM0和DOM2级事件的区别

Dom0级事件

dom0级有两种形式:

  • 在标签内绑定事件,如‘onclick=fn()’
  • 在js中用‘onclick=function(){}’的形式绑定

看看下面的例子:

1
2
3
4
5
6
7
8
9
10
<a id='test' href='javascript:void(0);' onclick='alert(click)'></a>
//js
var oTest = document.getElementById('oTest');
oTest.onclick = function(){
alert('testing...')
}
oTest.onclick = function(){
alert('finished!')
}

上面的例子执行后,你会发现只会弹出finished字样的提示框,这是因为同一类型的事件只能绑定一个,后面会覆盖前面的。

Dom2级事件

Dom2级事件有两个方法:addEventListener()和removeEventListener(),

都可传入3个参数:

  • 事件名,不需要加on
  • 事件函数
  • capture标识,true或false,默认false(冒泡)

我们将上面例子用Dom2级改写后就能顺序输出testing和finished字样了:

1
2
3
4
5
6
7
8
9
10
<a id='test' href='javascript:void(0);'></a>
//js
var oTest = document.getElementById('oTest');
oTest.addEventListener('click', function(){
alert('testing...')
})
oTest.addEventListener('click', function(){
alert('finished!')
})

移除事件时需要传入和绑定事件时完全相同的参数,所以匿名函数绑定的事件不能被移除

IE 通过attachEvent()和detachEvent()来支持,只传入事件名(要加on前缀)和处理函数,只支持冒泡