事件发生时,你在想什么?

本章内容:javascript

  • 理解事件流
  • 使用事件处理程序
  • 不一样的事件类型

事件流

事件流描述的是从页面中接受事件的顺序。但有意思的是,IE和Netscape开发团队竟然提出了差很少彻底相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流。html

事件冒泡(event bubbling)

即事件开始时由最具体的元素接收,而后逐级向上传播到较为不具体的节点。java

全部现代浏览器都支持事件冒泡,但在具体实现上仍是有一些差异。IE5.5及更早版本中的事件冒泡会跳过<html>元素(从<body>直接跳到document)。IE九、Firefox、Chrome和Safari则将事件一致冒泡到window对象。android

事件捕获(event capturing)

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。ios

DOM事件流

DOM2级事件规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的是事件捕获,为截获事件提供了机会。而后是实际的目标接收到事件。最后一个阶段是冒泡阶段,能够在这个阶段对事件作出响应。
多数支持DOM事件流的浏览器都实现了一种特定的行为;即便“DOM2级事件”规范明确要求捕获阶段不会涉及事件目标,但IE九、Safari、Firefox和Opera9.5及更高版本都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操做事件。git

事件处理程序

事件就是用户或浏览器自身执行的某种动做。诸如click、load和mouseover,都是事件的名字。而响应某个事件的函数就是事件处理程序。事件处理程序的名字以“on”开头,所以click事件的事件处理程序就是onclick,load事件的事件处理程序就是onload。为事件指定处理程序的方式有好几种。github

HTML事件处理程序

<script type='text/javascript'>
    function showMessage() {
        alert('hello world');
    }
</script>
<input type='button' value='Click Me' onclick='showMessage()' />
复制代码

在这个例子中,单击按钮就会调用showMessage()函数。这个函数是在一个独立的<script>元素中定义的,固然也能够被包含在一个外部文件中。事件处理程序中的代码在执行时,有权访问全局做用域中的任何代码。web

这样指定事件处理程序具备一些独到之处。首先,这样会建立一个封装着元素属性值的函数。这个函数中有一个局部变量event,也就是事件对象:api

<input type='button' value="Click Me" onClick='alert(event.type)'>
复制代码

经过event变量,能够直接访问事件对象,你不用本身定义它,也不用从函数的参数列表中读取。在这个函数内部,this值等于事件的目标元素。数组

<input type='button' value='Click Me' onclick='alert(this.value)'>
复制代码

关于这个动态建立的函数,另外一个有意思的地方是它扩展做用域的方式。在这个函数内部,能够像访问局部变量同样访问document及该元素自己的成员。这个函数使用with像下面这样扩展做用域:

function () {
    with(document) {
        with(this) {
            // 元素属性值
        }
    }
}
复制代码

如此一来,事件处理程序要访问本身的属性就简单多了。下面这行代码与前面的例子效果相同:

<input type='button' value='Click Me' onclick='alert(value)'>
复制代码

若是当前元素是一个表单输入元素,则做用域中还会包含访问表单元素的入口,这个函数就变成了以下所示:

<form method='post'>
    <input type='text' name='username' value=''>
    <input type='button' value='Echo Username' onclick='alert(username.value)'>
</form>
复制代码

在这个例子中,单击按钮会显示文本框中的文本。值得注意的是,这里直接引用了username元素。
不过,在HTML中指定事件处理程序有两个缺点。

  • 存在一个执行顺序的问题。由于用户可能会在HTML元素一出如今页面上就触发相应的事件,但当时的事件处理程序有可能尚不具有执行条件。之前面的例子来讲明。
<input type='button' value='Click Me' onclick='showMessage()' />

<script type='text/javascript'>
    function showMessage() {
        alert('hello world');
    }
</script>
复制代码

若是用户在页面解析showMessage()函数以前就单击了按钮,就会引起错误。为此,不少HTML事件处理程序都会被封装在一个try-catch块中,以便错误不会浮出水面。

<input type='button' value='Click Me' onclick='try{showMessage()}catch(ex){}'>
复制代码
  • 另外一个缺点就是,这样扩展事件处理程序的做用域链在不一样浏览器中会致使不一样结果。不一样Javascript引擎遵循的标识符解析规则略有差别,极可能会在访问非限定对象成员时出错。

  • 最后一个缺点是HTML与Javascript代码紧密耦合。若是要更换事件处理程序,就要改动两个地方:HTML代码和Javascript代码。

DOM0级事件处理程序

经过JavaScript指定事件处理程序的传统方式,就是讲一个函数赋值给一个事件处理程序属性。这种为事件处理程序赋值的方法是在第四代Web浏览器中出现的,并且至今仍然为全部现代浏览器所支持。缘由:

  • 简单
  • 跨浏览器的优点

每一个元素都有本身的事件处理程序属性,这些属性一般所有小写,例如onclick。将这种属性的值设置为一个函数,就能够指定事件处理程序:

var btn = document.getElementById('myBtn');

btn.onclick = function () {
    alert('Clicked');
}
复制代码

有一些缺点:
在这些代码运行之前不会指定事件处理程序,所以若是这些代码在页面中位于按钮后面,就有可能在一段时间内怎么单击都没反应。

使用DOM0级方法指定的事件处理程序被认为是元素的方法。所以,这时候的事件处理程序是在元素的做用域中运行;换句话说,程序中的this引用当前元素。

<input type='button' id='myBtn'>

var btn = document.getElementById('myBtn');

btn.onclick = function () {
    alert(this.id);
}
复制代码

单击按钮显示的是元素的ID,这个ID是经过this.id取得的。不只仅是ID,实际上能够在事件处理程序中经过this访问元素的任何属性和方法。以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
也能够删除经过DOM0级方法指定的事件处理程序,只要像下面这样将事件处理程序属性的值设置为null便可:

btn.onclick = null;
复制代码

DOM2级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操做:addEventListener()和removeEventListener()。全部DOM节点中都包含这两个方法,而且它们都接收3个参数:要处理的事件名、做为事件处理程序的函数和一个布尔值。最后这个布尔值参数若是是true,表示在捕获阶段调用事件处理程序;若是是false,表示在冒泡阶段调用事件处理程序。

要在按钮上为click事件添加事件处理程序,可使用下列代码:

var btn = document.getElementById('myBtn');

btn.addEventListener('click', function () {
    alert(this.id);
}, false);
复制代码

上面的代码为一个按钮添加了onclick事件处理程序,并且该事件会在冒泡阶段被触发。与DOM0级方法同样,这里添加的事件处理程序也是在其依附的元素的做用域中运行。使用DOM2级方法添加事件处理程序的好处是能够添加多个事件处理程序。来看下面的例子。

var btn = document.getElementById('MyBtn');

btn.addEventListener('click', function () {
    alert('Hello world');
}, false);
复制代码

经过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着经过addEventListener()添加的匿名函数将没法移除

var btn = document.getElementById('myBtn');

btn.addEventListener('click', function () {
    alert(this.id);
}, false)

// 这里省略了其余代码
btn.removeEventListener('click', function () {
    alert(this.id);
}, false)  // 这样无效


var handler = function ()  {
    alert(this.id);
}

btn.addEventListener('click', handler, false)


btn.removeEventListener('click', handler, false)  // 这样有效
复制代码

大多数状况下,都是将事件处理程序添加到事件流的冒泡阶段,这样能够最大限度地兼容各类浏览器。最好只在须要在事件到达目标以前截获它的时候将事件处理程序添加到捕获阶段。若是不是特别须要,咱们不建议在事件捕获阶段注册事件处理程序。

IE事件处理程序(若是放弃IE8及如下能够略过)

IE实现了与DOM中相似的两个方法:attachEvent()和detachEvent()。这两个方法接收相同的两个参数:事件处理程序与事件处理程序函数。因为IE8及更早版本只支持事件冒泡,因此经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段。

要使用attachEvent()为按钮添加一个事件处理程序,可使用如下代码。

var btn = docunment.getElementById('myBtn');
btn.attachEvent('onclick', function () {
    alert('Clicked');
})
复制代码

在IE中使用attachEvent()与使用DOM0级方法的主要区别在于事件处理程序的做用域。在使用DOM0级方法的状况下,事件处理程序会在全局做用域运行,所以this等于window。

来看下面的例子。

var btn = document.getElementById('myBtn');
btn.attachEvent('onclick', function () {
    alert(this=== window);  // true
})
复制代码

在编写跨浏览器的代码时,牢记这一区别很是重要。

使用attachEvent()添加的事件能够经过detachEvent()来移除,条件是必须提供相同的参数。与DOM方法同样,这也意味着添加的匿名函数将不能被移除。不过,只要可以将对相同函数的引用传给dtachEvent(),就能够移除相应的事件处理程序。

跨浏览器的事件处理程序

为了以跨浏览器的方式处理事件,很多开发人员会使用可以隔离浏览器差别的JavaScript库。

var EventUtil = {
    addHandler: function (element, type, handler) {
        if (element.addEventListener) {
         element.addEventListener(type, handler, false)
        } else if (element.attachEvent) {
            element.attachEvent('on' + type, handler);
        } else {
            element['on' + type] = handler;
        }
    },
    removeHandler: function (element, type, handler) {
        if (element.removeEventListener) {
            element.removeEventListener(type, handler, false)
        } else if (element.detachEvent) {
            element.detach('on'+ type, handler);
        } else {
            element['on' + type] = null;
        }
    }
}

复制代码

事件对象

在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着全部与事件有关的信息。包括致使事件的元素、事件的类型以及其余与特定事件相关的信息。

DOM中的事件对象

兼容DOM的浏览器会将一个event对象传入到事件处理程序中。不管指定事件处理程序时使用什么方法(DOM0级或DOM2级),都会传入event对象。

var btn = document.getElementById('myBtn');

btn.onclick = function (event) {
    alert(event.type)  //'click'
}

btn.addEventListener('click', function (event) {
    alert(event.type)   //'click'
}, false);
复制代码

这个例子中的两个事件处理程序都会弹出一个警告框,显示由event.type属性表示的事件类型。这个属性始终都会包含被触发的事件类型。

event对象包含与建立它的特定事件有关的属性和方法。触发的事件类型不同,可用的属性和方法也不同。不过,全部事件都会有下表列出的成员。

属性/方法 类型 读/写 说明
preventDefault() Function 只读 取消事件的默认行为。若是cancelable是true,则可使用这个方法
stopImmediatePropagation() Function 只读 取消事件的进一步捕获或冒泡。同时阻止任何事件处理程序被调用(DOM3级事件中新增)
stopPropagation() Function 只读 取消事件的进一步捕获或冒泡。若是bubbles为true,则可使用这个方法
target Element 只读 事件的目标
trusted Boolean 只读 为true表示事件是浏览器生成的。为false表示事件是由开发人员经过Javascript建立的(DOM3级事件中新增)
type String 只读 被触发的事件的类型
view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象

看下面例子:

document.body.onclick = function (event) {
    alert(event.currentTarget === document.body);  // true
    alert(this === document.body) // true
    alert(event.target === document.getElementById('myBtn')); // true
};
复制代码

当单击这个例子中的按钮时,this和currentTarget都等于document.body,由于事件处理程序是注册到这个元素上的。然而,target元素却等于按钮元素,由于它是click事件真正的目标。因为按钮上并无注册事件处理程序,结果click事件就冒泡到document.body,在那里事件才获得了处理。

在须要经过一个函数处理多个事件时,可使用type属性。例如:

var btn = document.getElementById('myBtn');

var handler = function (event) {
    switch (event.type) {
        case 'click':
            alert('Clicked');
            break;
        
        case 'mouseover':
            event.target.style.backgroundColor = 'red';
            break;
            
        case 'mouseout':
            event.target.style.backgroundColor = '';
            break;
    }
};

btn.onclick = handler;
btn.onmouseover = handler;
btn.onmouseout = handler;
复制代码

要阻止特定事件的默认行为,可使用preventDefault()方法。例如,连接的默认行为就是在被单击时会导航到其href特性指定的URL。若是你想阻止连接导航这一默认行为,那么经过连接onclick事件处理程序能够取消它。

只有cancelable属性设置为true的事件,才可使用preventDefault()来取消其默认行为。

另外,stopPropagation()方法用于当即中止事件在DOM层次中的传播,即取消进一步的事件捕获或冒泡。例如,直接添加到一个按钮的事件处理程序能够调用stopPropagation(),从而避免触发注册在document.body上面的事件处理程序,以下面的例子所示。

事件对象的eventPhase属性,能够用来肯定事件当前正位于事件流的哪一个阶段。若是是在捕获阶段调用的事件处理程序,eventPhase等于1;若是事件处理程序处于目标对象上,则eventPhase等于2;若是在冒泡阶段调用的事件处理程序,eventPhase等于3。
注意,尽管“处于目标”发生在冒泡阶段,但eventPhase仍然一直等于2。

当eventPhase等于2时,this、target和currentTarget始终是相等的。

只有在事件处理程序执行期间,event对象才会存在;一旦事件处理程序执行完成,event对象就会被销毁。

IE中的事件对象

与访问DOM中的event对象不一样,要访问IE中的event对象有几种不一样的方式,取决于指定事件处理程序的方法。在使用DOM0级方法添加事件处理程序时,event对象做为window对象的一个属性存在。来看下面的例子。

var btn = document.getElementById('myBtn');
btn.onclick = function () {
    var event = window.event;
    alert(event.type)  //'click'
}
复制代码

但是,若是事件处理程序是使用attachEvent()添加的,那么就会有一个event对象做为参数被传入事件处理程序函数中,以下所示。

var btn = document.getElementById('myBtn');
btn.attacehEvent('onclick', function (event) {
    alert(event.type);  //'click'
})
复制代码

在像这样使用attachEvent()状况下,也能够经过window对象来访问event对象,就像使用DOM0级方法时同样。不过为方便起见,同一个对象也会做为参数传递。

若是是经过HTML特性指定的事件处理程序,那么还能够经过一个名叫event的变量来访问event对象(与DOM中的事件模型相同)。

<input type='button' value='Click Me' onclick='alert(event.type)' >
复制代码

IE的event对象一样也包含与建立它的事件相关的属性和方法。其中不少属性和方法都有对应的或者相关的DOM属性和方法。与DOM的event对象同样,这些属性和方法也会由于事件类型的不一样而不一样,但全部事件对象都会包含下表所列的属性和方法。

属性/方法 类型 读/写 说明
cancelBubble Boolean 读/写 默认值false,但将其设置为true就能够取消事件冒泡(与DOM中的stopPropagation()方法的做用相同)
returnValue Boolean 读/写 默认值为true,将其设置为false就能够取消事件的默认行为(与DOM中的preventDefault()方法的做用相同)
srcElement Element 只读 事件的目标(与DOM中的target属性相同)
type String 只读 被触发的事件的类型

由于事件处理程序的做用域是根据指定它的方式来肯定的,因此不能认为this会始终等于事件目标。故而,最好仍是使用event.srcElement比较保险。

跨浏览器的事件对象

虽然DOM和IE中的event对象不一样,但基于它们之间的类似性依旧能够拿出跨浏览器的方案来。IE中event对象的所有信息和方法DOM对象中都有,只不过实现方式不同。不过,这种对应关系让实现两种事件模型之间的映射很是容易。能够对前面介绍的EventUtil对象加以加强,添加以下方法以求同存异。

var EventUtil = {
    addHandler: function (element, type, handler) {},
    removeHandler: function (element, type, handler) {},
    getEvent: function (event) {
        return event ? event : window.event;
    },
    getTarget: function (event) {
        return event.target || event.srcElement;
    },
    preventDefault: function (event) {
        if (event.preventDefault) {
            event.preventDefault();
        } else {
            event.returnValue = false;
        }
    },
    stopPropagation: function (event) {
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    }
}

复制代码

跨浏览器阻止事件冒泡:

var btn = document.getElementById('myBtn');

btn.onclick = function (event) {
    alert('Clicked');
    
    event = EventUtil.getEvent(event);
    EventUtil.stopPropagation(event);
}

document.body.onclick = function (event) {
    alert('Body clicked');
}
复制代码

事件类型

Web浏览器中可能发生的事件有不少类型。

“DOM3级事件”规定了如下几类事件。

  • UI事件,当用户与页面上的元素交互时触发。
  • 焦点事件,当元素得到或失去焦点时触发。
  • 鼠标事件,当用户经过鼠标在页面上执行操做时触发。
  • 滚轮事件
  • 文本事件
  • 键盘事件
  • 合成事件,**当为IME(Input Method Editor)输入字符时触发
  • 变更(mutation)事件,当底层DOM结构发生变化时触发
  • 变更名称事件,当元素或属性名变更时触发。此类事件已经被废弃,没有任何浏览器实现它们

UI事件

UI事件指的是那些不必定与用户操做有关的事件。这些事件在DOM规范出现以前,都是以这个或那种形式存在的,而在DOM规范中保留是为了向后兼容。
现有的UI事件以下:

  • DOMActivate:表示元素已经被用户操做激活。
  • load:当页面彻底加载后在window上面触发,当全部框架都加载完毕时在框架集上面触发,当图像加载完毕时在元素上触发,或者当嵌入的内容加载完毕时在元素上面触发。
  • unload:当页面彻底卸载后在window上面触发,当全部框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在元素上面触发。
  • abort:在用户中止下载过程时,若是嵌入的内容没有加载完,则在元素上面触发。
  • error:当发生Javascript错误时在window上面触发,当没法加载图像时在元素上面触发,当没法加载嵌入内容时在元素上面触发,或者当有一或多个框架没法加载时在框架集上面触发。
  • select:当用户选择文本框(<input>或<textarea>)中的一或多个字符时触发。
  • resize:当窗口或框架的大小变化时在window或框架上面触发。
  • scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的滚动条。
  • 多数这些事件都与window对象或表单控件相关。

    要肯定浏览器是否支持DOM2级事件规定的HTML事件,可使用以下代码:

    var isSupported = document.implementation.hasFeature('HTMLEvents', '2.0');
    复制代码

    load事件

    最经常使用的一个事件。当页面彻底加载后(包括全部图像、JavaScript文件、CSS文件等外部资源),就会触发window上面的load事件。

    有两种定义onload事件处理程序的方式。
    第一种方式是使用以下所示的JavaScript代码:

    EventUtil.addHandler(window, 'load', function (event) {
        alert('Loaded!');
    })
    复制代码

    第二种指定onload事件处理程序的方式是为<body>元素添加一个onload特性:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Load Event Example</title>
    </head>
    <body onload='alert("loaded!")'>
    </body>
    </html>
    复制代码

    通常来讲,在window上面发生的任何事件均可以在<body/>元素中经过相应的特性来指定,由于在HTML中没法访问window元素。实际上,这只是为了保证向后兼容的一种权宜之计。建议尽量使用javascript方式。

    图像上面也能够触发load事件,不管是在DOM中的图像元素仍是HTML中的图像元素。所以,能够在HTML中为任何图像指定onload事件处理程序:

    <img src='smile.gif' onload='alert("Image loaded.")'>
    复制代码

    这样,当例子中的图像加载完毕后就会显示一个警告框。一样的功能可使用JavaScript实现:

    var $image = document.getElementById('myImg');
    $image.addEventListener('load', function (e) {
        alert('image loaded');
    })
    复制代码

    还有一些元素也以非标准的方式支持load事件。在IE9+、Firefox、Opera、Chrome和Safari 3+及更高版本中,<script>元素也会触发load事件,以便开发人员肯定动态加载的JavaScript文件是否加载完毕。与图像不一样,只有设置了<script>元素的src属性并将元素添加到文档后,才会开始下载JavaScript文件。换句话说,对于<script>元素而言,指定src属性和指定事件处理程序的前后顺序就不重要了。

    unload事件

    与load事件对应的是unload事件,这个事件在文档被彻底卸载后触发。只要用户从一个页面切换到另外一个页面,就会发生unload事件。而利用这个事件最懂的状况是清除引用,以免内存泄漏。

    **不管使用那种方式,都要当心编写onunload事件处理程序中的代码。既然unload事件是在一切都被卸载以后才触发,那么在页面加载后存在的那些对象,此时就不必定存在了。此时,操做DOM节点或者元素的样式就会致使错误。

    resize事件

    当浏览器窗口被调整到一个新的高度或宽度时,就会触发resize事件。

    关于什么时候会触发resize事件,不一样浏览器有不一样的机制。IE、Safari、Chrome和Opera会在浏览器窗口变化了1像素就触发resize事件,而后随着变化不断重复触发。Firefox则只会在用户中止调整窗口大小时才会触发resize事件。

    scroll事件

    虽然scroll事件是在window对象上发生的,但它实际表示的则是页面中相应元素的变化。
    **在混杂模式下,能够经过<body>元素的scrollLeft和scrollTop来监控到这一变化;而在标准模式下,除Safari以外的全部浏览器都会经过<html>元素来反映这一变化(Safari仍然基于<body>跟踪滚动位置)。 **

    与resize事件相似,scroll事件也会在文档被滚动期间重复被触发,因此有必要尽可能保持事件处理程序的代码简单。

    焦点事件

    焦点事件会在页面得到或失去焦点时触发。利用这些事件并与document.hasFocus()方法及document.activeElement属性配合,能够知晓用户在页面上的行踪,有如下6个焦点事件。

    • blur:在元素失去焦点时触发。这个事件不会冒泡;全部浏览器都支持它。
    • DOMFocusIn: 只有opera支持
    • DOMFocusOut: 只有opera支持
    • focus:在元素得到焦点时触发,不会冒泡
    • focusin:在元素得到焦点时触发,与focus等价,可是会冒泡
    • focusout: 在元素失去焦点时触发

    这一类事件中最主要的两个是focus和blur,它们都是javscript早期就获得全部浏览器支持的事件。这些事件的最大问题是不冒泡。所以,IE的focusin和focusout与Opera的DOMFocusIn和DOMFocusOut才会重叠。IE的方式最后被DOM3级事件采纳为标准方式。

    当焦点从页面中的一个元素移动到另外一个元素,会依次触发下列事件:

    • focusout在失去焦点的元素上触发
    • focusin在得到焦点的元素上触发
    • blur在失去焦点的元素上触发
    • DOMFocusOut在失去焦点的元素上触发
    • focus在得到焦点的元素上触发
    • DOMFocusIn在得到焦点的元素上触发 其中,blur、DOMFocusOut和focusout的事件目标是失去焦点的元素;而focus、DOMFocusIn和focusin的事件目标是得到焦点的元素。

    即便focus和blur不冒泡,也能够在捕获阶段侦听它们。

    下面演示全局侦听blur和focus

    鼠标与滚轮事件

    鼠标事件是Web开发中最经常使用的一类事件。DOM3级事件中定义了9个鼠标事件。

    • click 略
    • dblclick 略
    • mousedown 略
    • mouseenter,在鼠标光标从元素外部首次移动到元素范围以内时触发。这个事件不冒泡,并且在光标移动到后代元素上不会触发。DOM2级事件并无定义这个事件,但DOM3级事件将它归入了规范。IE、Firefox 9+和Opera支持这个事件。
    • mouseleave,在位于元素上方的鼠标光标移动到元素范围以外时触发。这个事件不冒泡,并且在光标移动到后代元素上不会触发。DOM2级事件并无定义这个事件,但DOM3级事件将它归入了规范。IE、Firefox 9+和Opera支持这个事件。
    • mousemove,当鼠标指针在元素内部移动时重复地触发。不能经过键盘触发这个事件。
    • mouseout,在鼠标指针位于一个元素上方,而后用户将其移入另外一个元素时触发。又移入的另外一个元素可能位于前一个元素的外部,也多是这个元素的子元素。不能经过键盘触发这个事件。
    • mouseover,在鼠标指针位于一个元素外部,而后用户将其首次移入另外一个元素边界以内时触发。
    • mouseup,在用户释放鼠标按钮时触发。不能经过键盘触发这个事件。

    页面中的全部元素都支持鼠标事件。除了mouseenter和mouseleave,全部鼠标事件都会冒泡,也能够被取消,而取消鼠标事件将会影响浏览器的默认行为。取消鼠标事件的默认行为还会影响其余事件,由于鼠标事件与其余事件是密不可分的关系。

    **只有在同一个元素上相继触发mousedown和mouseup事件,才会触发click事件;若是mousedown或mouseup中的一个被取消,就不会触发click事件。相似地,只有触发两次click事件,才会触发一次dblclick事件。若是有代码阻止了连续两次触发click事件,那么就不会触发dblclick事件了。

    这4个事件触发的顺序始终以下:

    • mousedown
    • mouseup
    • click
    • mousedown
    • mouseup
    • click
    • dblclick

    使用如下代码能够检测浏览器是否支持以上DOM2级事件(除dbclick、mouseenter和mouseleave以外):

    var isSupported = document.implementation.hasFeature('MouseEvents', '2.0');
    复制代码

    要检测浏览器是否支持上面的全部事件,可使用如下代码:

    var isSupported = document.implementation.hasFeature('MouseEvents', '3.0');
    复制代码

    鼠标事件中还有一类滚轮事件。而说是一类事件,其实就是一个mousewheel事件。这个事件跟踪鼠标滚轮,相似于Mac的触控板。

    客户区坐标

    鼠标事件都是在浏览器视口中特定的位置上发生的。这个位置信息保存在事件对象的clientX和clientY属性中。全部浏览器都支持这两个属性。

    页面坐标位置

    经过客户区坐标可以知道鼠标是在视口中什么位置发生的,而页面坐标经过事件对象的pageX和pageY属性,能告诉你事件是在页面中的什么位置发生的。换句话说,这两个属性表示鼠标光标在页面中的位置,所以坐标是从页面自己而非视口的左边和顶边计算的。

    屏幕坐标位置

    鼠标事件发生时,不只会有相对于浏览器窗口的位置,还有一个相对于整个电脑屏幕的位置。而经过screenX和screenY属性就能够肯定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息。

    修改键

    虽然鼠标事件主要是使用鼠标来触发的,但在按下了鼠标时键盘上的某些键的状态也能够影响到所要采起的操做。这些修改键就是Shift、Ctrl、Alt和Meta,它们常常被用来修改鼠标事件的行为。DOM为此规定了4个属性,表示这些修改键的状态:shiftKey、ctrlKey、altKey和metaKey。

    相关元素

    在发生mouseover和mouseout事件时,还会涉及更多的元素。这两个事件都涉及把鼠标指针从一个元素的边界以内移动到另外一个元素的边界以内。对mouseover事件而言,事件的主目标是得到光标的元素,而相关元素就是那个失去光标的元素。相似的,对mouseout事件而言,事件的主目标是失去光标的元素,而相关元素则是得到光标的元素。

    DOM经过event对象的relatdTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;对于其余事件,这个属性的值是null。IE8及以前版本不支持relatedTarget属性,但提供了保存着一样信息的不一样属性。在mouseover事件触发时,IE的fromElement属性中保存了相关元素;在mouseout事件触发时,IE的toElement属性中保存着相关元素。

    鼠标按钮

    只有在主鼠标按钮被单击时才会触发click事件,所以检测按钮的信息并非必要的。但对于mousedown和mouseup事件来讲,则在其event对象存在一个button属性,表示按下或释放的按钮。DOM的button属性可能有以下3个值:0表示主鼠标按钮,1表示中间的鼠标按钮,2表示次鼠标按钮。在常规的设置中,主鼠标按钮就是鼠标左肩,而次鼠标按钮就是鼠标右键。

    在使用onmouseup事件处理程序时,button的值表示释放的是哪一个按钮。此外,若是不是按下或释放了主鼠标按钮,Opera不会触发mouseup或mousedown事件。

    更多的事件信息

    “DOM2级事件”规范在event对象中还提供了detail属性,用于给出有关事件的更多信息。对于鼠标事件来讲,detail中包含了一个数值,表示在给定位置上发生了多少次单击。在同一个像素上相继地发生一次mousedown和一次mouseup事件算做一次单击。detail属性从1开始计数,每次单击发生后都会递增。若是鼠标在mousedown和mouseup之间移动了位置,则detail会被重置为0

    鼠标滚轮事件

    IE6.0首先实现了mousewheel事件。此后,Opera、Chrome和Safari也都实现了这个事件。当用户经过鼠标滚轮与页面交互、在垂直方向上滚动页面时,就会触发mousewheel事件。这个事件能够在任何元素上面触发,最终会冒泡到document或window对象。与mousewheel事件对应的event对象除包含鼠标事件的全部标准信息外,还包含一个特殊的wheelDelta属性。当用户向前滚动鼠标滚轮时,wheelDelta是120的倍数;当用户向后滚动鼠标滚轮时,wheelDelta是-120倍数。

    触摸设备

    IOS和android设备的实现很是特别,由于这些设备没有鼠标。在面向iPhone和iPod中的Safari开发时,要记住如下几点:

    • 不支持dblclick事件。双击浏览器窗口会放大画面,并且没有办法改变该行为
    • 轻击可单击元素会触发mousemove事件。若是此操做会致使内容变化,将再也不有其余事件发生;若是屏幕没有所以变化,那么会依次发生mousedown、mouseup和click事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操做的元素(如连接),或者那些已经被指定了onclick事件处理程序的元素。
    • 两个手指放在屏幕上且页面随手指移动而滚动时会触发mousewheel和scroll事件。

    键盘与文本事件

    用户在使用键盘时会触发键盘事件。“DOM2级事件”最初规定了键盘事件,但在最终定稿以前又删除了相应的内容。结果,对键盘事件的支持主要遵循的是DOM0级。

    “DOM3级事件”为键盘事件指定了规范,IE9率先彻底实现了该规范。其余浏览器也在着手实现这一标准,但仍然有不少问题。

    • keydown:当用户按下键盘上的任意键时触发,并且若是按住不放的话,会重复触发。
    • keypress: 当用户按下键盘上的字符键时触发,并且若是按住不放的话,会重复触发此事件。
    • keyup: 当用户释放键盘上的键时触发。
      虽然全部元素都支持以上3个事件,但只有在用户经过文本框输入文本时才最经常使用到。

    只有一个文本事件:textInput。这个事件是对keypress的补充,用意是在将文本显示给用户以前更容易拦截文本。在文本插入文本框以前会触发textInput事件。

    在用户按了一下键盘上的字符键时,首先会触发keydown事件,而后紧跟着是keypress事件,最后会触发keyup事件。其中,keydown和keypress都是在文本框发生变化以前被触发的;而keyup事件则是在文本框已经发生变化以后被触发的。若是用户按下了一个字符键不放,就会重复触发keydown和keypress事件,直到用户松开该键为止。

    若是用户按下的是一个非字符键,那么首先会触发keydown事件,而后是keyup事件。若是按着不放,就会一直重复触发keydown事件,直到用户松开这个键,此时会触发keyup事件。

    键盘事件与鼠标事件同样,都支持相同的修改键。并且,键盘事件的事件对象中也有shiftKey、ctrlKey、altKey和metaKey属性。IE不支持metaKey。

    键码

    在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符键,keyCode属性的值与ASCII码中对应下泄字母或数字的编码相同。

    所以,数字键7的keyCode值为55,而字母A键的keyCode值为65 ———— 与Shift键的状态无关。

    字符编码

    发生keypress事件意味着按下的键会影响到屏幕中文本的显示。在全部浏览器中,按下可以插入或删除字符的键都会触发keypress事件;按下其余键可否触发此事件因浏览器而异。

    DOM3级变化

    尽管全部浏览器都实现了某种形式的键盘事件,DOM3级事件仍是作出了一些改变。好比,DOM3级事件中的键盘事件,再也不包含charCode属性,而是包含两个新属性:key和char。

    其中,key属性是为了取代keyCode而新增的,它的值是一个字符串。在按下某个字符键时,key的值就是相应的文本字符(如k或M);在按下非字符键时,key的值是相应键的名(如Shift或Down)。而char属性在按下字符键时的行为与key相同,但在按下非字符键时值为null。
    DOM3级事件还添加了一个名为location的属性,这是一个数值,表示按下了什么位置上的键:

    • 0表示默认键盘
    • 1表示左侧位置(如左位的Alt键)
    • 2表示右侧位置(右侧的Shift键)
    • 3表示数字小键盘
    • 4表示移动设备键盘(也就是虚拟键盘)
    • 5表示手柄

    textInput事件

    “DOM3级事件”规范中引入了一个新事件,名叫textInput。根据规范,当用户在可编辑区域中输入字符时,就会触发这个事件。这个用于替代keypress的textInput事件的行为稍有不一样。

    • 区别之一就是任何能够得到焦点的元素均可以触发keypress事件,但只有可编辑区域才能触发textInput事件。
    • 区别之二是textInput事件只会在用户按下可以输入实际字符的键时才会被触发,而keypress事件则在按下那些可以影响文本显示的键时也会触发(如退格键)。

    因为textInput事件主要考虑的是字符,所以它的event对象中包含一个data属性,这个属性的值就是用户输入的字符(而非字符编码)。换句话说,用户在没有按上档键的状况下按下了S键,data的值就是‘s',而若是在按住上档键时按下该键,data的值就是'S'。

    设备中的键盘事件

    任天堂Wii会在用户按下Wii遥控器上的按键时触发键盘事件。

    复合事件(composition event)

    复合事件是DOM3级事件中新添加的一类事件,用于处理IME的输入序列。
    IME(Input Method Editor,输入法编辑器)可让用户输入在物理键盘上找不到的字符。例如,使用拉丁文键盘的用户经过IME照样能输入日文字符。IME一般须要同时按住多个键,但最终只输入一个字符。复合事件就是针对检测和处理这种输入而设计的。

    • compositionstart: 在IME的输入法打开时触发,表示要开始输入了。
    • compositionupdate: 在向输入字段中插入新字符时触发。
    • compositionend:在IME的输入法关闭时触发,表示返回正常键盘输入状态。

    复合事件与文本事件有不少方面都很类似。在触发复合事件时,目标是接收文本的输入字段。但它比文本事件的事件对象多一个属性data,其中包含如下几个值中的一个:

    • 若是在compositionstart事件发生访问,包含正在编辑的文本。
    • 若是在compositionupdate事件发生时访问,包含正插入的新字符。
    • 若是在compositionend事件发生时访问,包含这次输入会话中插入的全部字符。

    利用监听compositionstart判断是否开启了输入法。从而实现体验较为良好兼容性较强的监控字数的控件。

    变更事件

    DOM2级的变更(mutation)事件能在DOM中的某一部分发生变化时给出提示。变更事件是为XML或HTML DOM设计的,并不特定于某种语言。DOM2级定义了以下变更事件。

    • DOMSubtreeModified:在DOM结构中发生任何变化时触发。这个事件在其余任何事件触发后都会触发
    • DOMNodeInserted:在一个节点做为子节点被插入到另外一个节点中时触发。
    • DOMNodeRemoved:在节点从其父节点中被移除时触发。
    • DOMNodeInertedIntoDocument:在一个节点被直接插入文档或经过子树间接插入文档以后触发。这个事件在DOMNodeInserted以后触发。
    • DOMNodeRemovedFromDocument:在一个节点被直接从文档中移除或经过子树间接从文档中移除以前触发。
    • DOMAttrModified:在特性被修改以后触发。
    • DOMCharacterDataModified:在文本节点的值发生变化时触发。

    使用下列代码能够检测出浏览器是否支持变更事件:

    var isSupported = document.implementation.hasFeature('MutationEvents', '2.0');
    复制代码

    删除节点时的触发顺序

    • 在使用removeChild()或replaceChild()从DOM中删除节点时,首先会触发DOMNodeRemoved事件。这个事件的目标(event.target)是被删除的节点,而event.relatedNode属性中包含着对目标节点父节点的引用。在这个事件触发时,节点还没有从其父节点删除,所以其parentNode属性仍然指向父节点。

    • 若是被移除的节点包含子节点,那么在其全部子节点以及这个被移除的节点上会相继触发DOMNodeRemovedFromDocument事件。但这个事件不会冒泡,因此只有直接指定给其中一个子节点的事件处理程序才会被调用。这个事件的目标是相应的子节点或者那个被移除的节点,除此以外event对象中不包含其余信息。

    • 紧随其后触发的是DOMSubtreeModified事件。这个事件的目标是被移除节点的父节点;此时的event对象也不会提供与事件相关的其余信息。

    插入节点时的触发顺序

    • 在使用appendChild()、replaceChild()或insertBefore()向DOM中插入节点时,首先会触发DOMNodeInserted事件。这个事件的目标是被插入的节点,而event.relatedNode属性中包含一个对父节点的引用。在这个事件触发时,节点已经被插入到了新的父节点中。这个事件是冒泡的,所以能够在DOM的各个层次上处理它。
    • 紧接着,会在新插入的节点上面触发DOMNodeInsertedIntoDocument事件。这个事件不冒泡,所以必须在插入节点以前为它添加这个事件处理程序。这个事件的目标是被插入的节点,除此以外event对象中不包含其余信息。
    • 最后一个触发的事件是DOMSubtreeModified,触发于新插入节点的父节点。

    HTML5事件

    DOM规范没有涵盖全部浏览器支持的全部事件。HTML5详尽列出了浏览器应该支持的全部事件。本节只讨论其中获得浏览器完善支持的事件,但并不是所有事件。

    contextmenu事件

    contextmenu这个事件,用以表示什么时候应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义菜单。

    因为contextmenu事件是冒泡的,所以能够为document指定一个事件处理程序,用以处理页面中发生的全部此类事件。这个事件的目标是发生用户操做的元素。在全部浏览器中均可以取消这个事件。

    这个事件的目标是发生用户操做的元素。在全部浏览器中均可以取消这个事件:在兼容DOM的浏览器中,使用event.preventDefault();在IE中,将event.returnValue()的值,设置为false。由于contextmenu事件属于鼠标事件,因此其事件对象中包含与光标位置有关的全部属性。一般使用contextmenu事件来显示自定义的上下文菜单,而使用onclick事件处理程序来隐藏该菜单。

    beforeunload事件

    之因此发生在window对象上的beforeunload事件,是为了让开发人员有可能在页面卸载前阻止这一操做。这个事件会在浏览器卸载页面以前触发,能够经过它来取消卸载并继续使用原有页面。可是,不能完全取消这个事件,由于那就至关于让用户没法离开当前页面了。为此,这个事件的意图是将控制权交给用户。

    DOMContentLoaded事件

    window的load事件会在页面中的一切都加载完毕时触发,但这个过程可能会由于要加载的外部资源过多而破费周折。而DOMContentLoaded事件则在造成完整的DOM树以后就会触发,不理会图像、JavaScript文件、CSS文件或其余资源是否已经下载完毕。

    DOMContentLoaded事件对象不会提供任何额外的信息(其target属性是document)。

    **对于不支持DOMContentLoaded的浏览器,咱们建议在页面加载期间设置一个时间为0毫秒的超时调用:

    setTimeout(function () {
        // 在此添加事件处理程序
    }, 0);
    复制代码

    这个代码的实际意思就是:"在当前JavaScript处理完成后当即运行这个函数。"在页面下载和构建期间,只有一个JavaScript处理过程,所以超时调用会在该过程结束时当即触发。为了确保这个方法有效,必须将其做为页面中的第一个超时调用;即使如此,也仍是没法保证在全部环境中该超时调用必定会早于load事件被触发。

    readystatechange事件

    IE为DOM文档中的某些部分提供了readystatechange事件。这个事件的目的是提供与文档或元素的加载状态有关的信息。但这个事件的行为有时候也很难预料。支持readystatechange事件的每一个对象都有一个readystate属性,可能有下列5个值中的一个:

    • uninitialized 未初始化: 对象存在但还没有初始化
    • loading 正在加载: 对象正在加载数据
    • loaded 加载完毕: 对象加载数据完成
    • interactive 交互: 能够操做对象了,但尚未彻底加载
    • complete 完成: 对象已经加载完毕了

    这些状态看起来很直观,但并不是全部对象都会经历readyState的这几个阶段。换句话说,若是某个阶段不适用某个对象,则该对象彻底可能跳过该阶段;并无规定哪一个阶段适用于哪一个对象。显然,这意味着readystatechange事件常常会少于4次,而readyState属性的值也不老是连续。

    对于document而言,值为“interactive”的readyState会在与DOMContentLoaded大体相同的时刻触发readystatechange事件。此时,DOM树已经加载完毕,能够安全地操做它了,所以就会进入交互interactive阶段。

    这个事件的event对象不会提供任何信息,也没有目标对象。

    在与load事件一块儿使用时,没法预测两个事件触发的前后顺序。在包含较多或较大的外部资源的页面中,会在load事件触发以前先进入交互阶段;而在包含较少或较小的外部资源的页面中,则很难说readystatechange事件会发生在load事件前面。

    让问题变得更复杂的是,交互阶段可能会早于也可能会晚于完成阶段出现,没法确保顺序。在包含较多外部资源的页面中,交互阶段更有可能早于完成阶段出现;而在页面中包含较少外部资源的状况下,完成阶段先于交互阶段出现的可能性更大。所以,为了尽量抢到先机,有必要同时检测交互和完成阶段。

    var handler =  function (e) {
        if (document.readyState == 'interactive' || document.readyState == 'complete') {
            document.removeEventListener('readystatechange', handler);
            alert('Content loaded');
        }
    document.addEventListener('readystatechange', handler);
    复制代码

    pageshow和pagehide事件

    Firefox和Opera有一个特性,名叫“往返缓存”(back-forward cache),能够在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不只保存着页面数据,还保存了DOM和JavaScript状态;其实是将整个页面都保存在了内存里。若是页面位于bfcache中,那么再次打开该页面时就不会触发load事件。尽管因为内存中保存了整个页面的状态,不触发load事件也不该该会致使什么问题,但为了更形象地说明bfcache行为,Firefox仍是提供了一些新事件。

    第一个事件就是pageshow,这个事件在页面显示时触发,不管该页面是否来自bgcache。在从新加载的页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状态彻底恢复的那一刻触发。

    另外值得注意的是,虽然这个事件的目标是document,但必须将其事件处理程序添加到window。

    (function () {
        var pageshowCount = 0;
    
        window.addEventListener('pageshow', function (e) {
            pageshowCount++;
            notifyMe('page show: ' + pageshowCount);
        })
    })()
    复制代码

    这个例子使用了私有做用域,以防止变量showCount进入全局做用域。当页面首次加载完成时,showCount的值为0。此后,每当触发pageshow事件,showCount的值就会递增并经过警告框显示出来。若是你在离开包含以上代码的页面以后,又单击“后退”按钮返回该页面,就会看到showCount每次递增的值。这是由于该变量的状态,乃至整个页面的状态,都被保存在了内存中,当你返回这个页面时,它们的状态获得了恢复。若是你单击了浏览器的“刷新”按钮,那么showCount的值就会被重置为0,由于页面已经彻底从新加载了。

    除了一般的属性以外,pageshow事件的event对象还包含了一个名为presisted的布尔值属性。若是页面被保存在了bfcache中,则这个属性的值为true;不然,这个属性的值为false。

    经过检测persisted属性,就能够根据页面在bfcache中的状态来肯定是否须要采起其余操做。
    与pageshow事件对于的是pagehide事件,该事件会在浏览器卸载页面的时候触发,并且是在unload事件以前触发。与pageshow事件同样,pagehide在document上面触发,但其事件处理程序必需要添加到window对象。这个事件的event对象也包含persisted属性,不过其用途稍有不一样。

    hashchange事件

    HTML5新增了hashchange事件,以便在URL的参数列表发生变化时通知开发人员。之因此新增这个事件,是由于在Ajax应用中,开发人员常常要利用URL参数列来保存状态或导航信息。

    **使用如下代码能够检测浏览器是否支持hashchange事件:

    var isSupported = ('onhashchange' in window);
    复制代码

    设备事件

    智能手机和平板电脑的普及,为用户与浏览器交互引入了一种新的方式,而一类新事件也应运而生。device event可让开发人员肯定用户在怎样使用设备。

    orientationonchange事件

    只要用户改变了设备的查看模式,就会触发orientationchange事件。此时的event对象不包含任何有价值的信息,由于惟一相关的信息能够经过window。orientation访问到。

    全部IOS设备都支持orientationchange事件和window。orientation属性。

    MozOrientation事件(已废弃)

    firefox提供。这个事件与ios中的orientationchange事件不一样,该事件只能提供一个平面的方向变化。因为MozOrientation事件是在window对象上触发的,因此可使用如下代码来处理。

    deviceorientation事件

    本质上,DeviceOrientation Event规范定义的deviceorientation事件与MozOrientation事件相似。它也是在加速计检测到设备方向变化时在window对象上触发,并且具备与MozOrientation事件相同的支持限制。
    触发deviceorientation事件时,事件对象中包含着每一轴相对于设备静止状态下发生变化的信息。事件对象包含如下5个属性。

    • alpha
    • beta
    • gamma
    • absolute
    • compassCalibrated

    devicemotion事件

    devicemotion事件。这个事件是要告诉开发人员设备何时移动,而不只仅是设备方向如何改变。
    事件对象包含如下属性:

    • acceleration: 一个包含x、y和z属性的对象,在不考虑重力的状况下,告诉你在每一个方向上的加速度。
    • accelerationIncludingGravity:一个包含x、y和z属性的对象,在考虑z轴天然重力加速度的状况下,告诉你在每一个方向上的加速度。
    • interval:以毫秒表示的时间值,必须在另外一个devicemotion事件触发前传入。这个值在每一个事件中应该是一个常量。
    • rotationRate:一个包含表示方向的alpha、beta和gamma属性的对象。

    触摸和手势事件

    触摸事件

    具体来讲有如下几个触摸事件:

    • touchstart:当手指触摸屏幕时触发;即便已经有一个手指放在了屏幕上也会触发。
    • touchmove:当手指在屏幕上滑动时连续地触发。在这个事件发生期间,调用preventDefault()能够阻止滚动。
    • touchend: 当手指从屏幕上移开时触发。
    • touchcancel:当系统中止跟踪触摸时触发。

    上面这几个事件都会冒泡,也均可以取消。虽然这些触摸事件没有在DOM规范中定义,但它们倒是以兼容DOM的方式实现的。所以,每一个触摸事件的event对象都提供了在鼠标事件中常见的属性。

    除了常见的DOM属性外,触摸事件还包含下列三个用于跟踪触摸的属性。

    • touches: 表示当前跟踪的触摸操做的Touch对象的数组
    • targetTouchs:特定于事件目标的Touch对象的数组
    • changedTouches:表示自上次触摸以来发生了什么改变的Touch对象的数组

    手势事件

    有三个手势事件,分别介绍以下。

    • gesturestart: 当一个手指已经按在屏幕上而另外一个手指又触摸屏幕时触发。
    • gesturechange:当触摸屏幕的任何一个手指的位置发生变化时触发。
    • gestureend:当任何一个手指从屏幕上面移开时触发。

    只有两个手指都触摸到事件的接收容器时才会触发这些事件。在一个元素上设置事件处理程序。若是另外一个手指又放在了屏幕上,则会先触发gesturestart事件,随后触发基于该手指的touchstart事件。若是一个或两个手指在屏幕上滑动,将会触发gesturechange事件。但只要有一个手指移开,就会触发gestureend事件,紧接着又会触发基于该手指的touchend事件。

    触摸事件和手势事件之间存在某种关系。当一个手指放在屏幕上时,会触发touchstart事件。若是另外一个手指又放在了屏幕上,则会先触发gesturestart事件,随后触发基于该手指的touchstart事件。若是一个或两个手指在屏幕上滑动,将会触发gesturechange事件。但只要有一个手指移开,就会触发gestureend事件,紧接着又会触发基于该手指的touchend事件。

    与触摸事件同样,每一个手势事件的event对象都包含着标准的鼠标事件属性:bubbles、cancelable、view、clientX、clientY、screenX、screenY、detail、altKey、shiftKey、ctrlKey、metaKey。此外,还包含两个额外的属性:rotation和scale。

    其中,rotation属性表示手指变化引发的旋转角度,负值表示逆时针旋转,正值表示顺时针旋转。而scale属性表示两个手指间距离的变化状况。

    触摸事件也会返回rotation和scale属性,但这两个属性只会在两个手指与屏幕保持接触时才会发生变化。通常来讲,使用基于两个手指的手势事件,要比管理触摸事件中的全部交互要容易得多。

    内存和性能

    在javascript中,添加到页面上的事件处理程序数量将直接关系到页面的总体运行性能。致使这一问题的缘由是多方面。首先,每一个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定全部事件处理程序而致使的DOM访问次数,会延迟整个页面的交互就绪时间。事实上,从如何利用好事件处理程序的角度出发,仍是有一些方法可以提高性能的。

    事件委托

    对“事件处理程序过多”问题的解决方案就是事件委托

    在document对象添加一个事件处理程序,用以处理页面上发生的某种特定类型的事件。这样作与采起传统的作法相比具备以下优势。

    • document对象很快就能够访问,并且能够在页面生命周期的任什么时候点上为它添加事件处理程序(无需等待DOMContentLoaded或load事件)。换句话说,只要可单击的元素呈如今页面上,就能够当即具有适当的功能。
    • 在页面中设置事件处理程序所需的时间更少。只添加一个事件处理程序所需的DOM引用更少,所花的时间也更少。
    • 整个页面占用的内存空间更少,可以提高总体性能。

    移除事件处理程序

    每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JavaScript代码之间就会创建一个链接。这种链接越多,页面执行起来就越慢。如前所述,能够采用事件委托技术,限制创建的链接数量。另外,在不须要的时候移除事件处理程序,也是解决这个问题的一种方案。内存中留有那些过期不用的“空事件处理程序”(dangling event handler),也是形成web应用程序内存与性能问题的主要缘由。

    在两种状况下。可能会形成上述问题。

    • 第一种状况就是从文档中移除带有事件处理程序的元素时。多是使用removeChild()和replaceChild()方法时。更多的是发生在使用innerHTML替换页面中某一部分的时候。若是带有事件处理程序的元素被innerHTML删除了,那么原来添加到元素中的事件处理程序极有可能没法被看成垃圾回收。
    • 卸载页面的时候。若是在页面被卸载以前咩有清理干净事件处理程序,那它们就会滞留内存中。

    模拟事件

    事件常常由用户操做或经过其余浏览器功能来触发。但不多有人直到,也可使用JavaScript在任意时刻来触发特定的事件,而此时的事件就如同浏览器建立的事件同样。

    DOM中的事件模拟

    参考customEvent api

    小结

    事件是将Javscript与网页联系在一块儿的主要方式。“DOM3级事件”规范和HTML5定义了常见的大多数事件。即便有规范定义了基本事件,但不少浏览器仍然在规范以外实现了本身的专有事件,从而为开发人员提供更多掌握用户交互的手段。

    在使用事件时,须要考虑以下一些内存与性能方面的问题。

    • 有必要限制一个页面中事件处理程序的数量,数量太多会致使占用大量内存,并且也会让用户感受页面反应不够灵敏。
    • 创建在事件冒泡机制之上的事件委托技术,能够有效地减小事件处理程序的数量。
    • 建议在浏览器卸载页面以前移除页面中的全部事件处理程序。

    事件是JavaScript中最重要的主题之一,深刻理解事件的工做机制以及它们对性能的影响相当重要。

相关文章
相关标签/搜索