在浏览器或文档某个元素发生某个特定情况的瞬间,会作为一个事件进行广播,我们可以对其添加监听来处理特定的事件。

事件流

事件流描述了页面中接收事件的顺序。

整个事件流包含了三个阶段:事件捕获阶段、事件目标阶段和事件冒泡阶段。

 <!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="myDiv">Click me</div>
</body>
</html>

如果我们点击了上面代码中的div元素,实际的事件流如下:

  1. 捕获阶段:1、2、3属于捕获阶段;
  2. 目标阶段:4属于目标阶段;
  3. 冒泡阶段:5、6、7属于冒泡阶段;

事件处理

在HTML中,事件的处理有多种方法可以用来实现。下面我们一一来看。

HTML事件处理

即直接在HTML中指定处理的函数:

 <!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="myDiv" onclick="alert(this.id)//myDiv">Click me</div>
</body>
</html>

也可以指定为一个外部定义的函数:

 <!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<script type="text/javascript">
function divClickHandler(event) {
console.log(this.id); // undefined
console.log(event.type); // click
}
</script>
<div id="myDiv" onclick="divClickHandler(event)">Click me</div>
</body>
</html>

注意这种方式的话,作用域是全局的,而上面的写法可以通过this.id取到点击的对象的id;

另外,事件会有一个名为event的参数,可以传递给处理的函数来得知更加丰富的事件信息。

DOM0级事件处理

实际上就是HTML事件处理的代码指定写法,如下:

 <!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="myDiv" onclick="divClickHandler(event)">Click me</div>
<script type="text/javascript">
function divClickHandler(event) {
console.log(this.id); // myDiv
console.log(event.type); // click
} var myDiv = document.getElementById("myDiv");
myDiv.onclick = divClickHandler;
</script>
</body>
</html>

使用该方法可以取消事件的绑定,如下:

 myDiv.onclick = null;

这种方式比较简单,但是无法绑定2个函数。

DOM2级事件处理

新的标准中,关于事件的处理采用了观察者模式,添加了addEventListener和removeEventListener两个方法,这两个方法都接受3个参数:

  1. 事件类型
  2. 处理该事件的函数
  3. 最后一个布尔值:true表示捕获阶段调用,false表示冒泡阶段调用,默认false,需要注意目标阶段也算在冒泡阶段中

我们看下例子:

 var divClickHandler = function(event) {
console.log(this.id); // myDiv
console.log(event.type); // click
} var myDiv = document.getElementById("myDiv");
myDiv.addEventListener("click", divClickHandler);

需要注意的是使用该方法可以为一个事件类型添加多个事件处理监听,使用removeEventListener可以移除添加了的监听,我们再看一个例子:

 <!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="myGroup">
<div id="myDiv">Click me</div>
</div>
<script type="text/javascript">
function buhuo(event) {
console.log(1);
}
function maopao(event) {
console.log(2);
} var myGroup = document.getElementById("myGroup");
myGroup.addEventListener("click", buhuo, true);
myGroup.addEventListener("click", maopao); function divClickHandler(event) {
console.log(3);
} var myDiv = document.getElementById("myDiv");
myDiv.addEventListener("click", divClickHandler);
</script>
</body>
</html>

点击后输出如下:

 1
3
2

IE事件处理

在早期的IE中,使用的是attachEvent和detachEvent,由于不是标准的方法,所以这里就不记录了,有兴趣的童鞋可以自行搜索。

事件对象

在触发DOM上的某个事件时,将会产生一个事件对象event,这个对象包含所有与当前事件有关的信息。

DOM中的事件对象

无论是DOM0还是DOM2级,都会传入event对象,如下:

 var myDiv = document.getElementById("myDiv");
myDiv.onclick = function(event) {
console.log(event.type); // click
}
myDiv.addEventListener("click", divClickHandler);
function divClickHandler(event) {
console.log(event.type); // click
}

即使直接写在HTML中(DOM0级的另一种写法),也一样存在event事件对象:

<div id="myDiv" onclick="console.log(event.type)">Click me</div>

target与currentTarget

在事件处理函数中,this对象始终指向currentTarget对象,target对象包含事件的实际目标(即处于目标阶段的对象)。而currentTarget对象指向添加了当前事件处理函数的对象。

我们来看下面的例子:

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<body id="myBody">
<div id="myGroup">
<div id="myDiv" onclick>Click me</div>
</div>
<script type="text/javascript">
var body = document.body;
var myGroup = document.getElementById("myGroup");
var myDiv = document.getElementById("myDiv"); body.addEventListener("click", onBodyClick);
function onBodyClick(event) {
console.log("click body: " + event.currentTarget.id + ", " + event.target.id);
} myGroup.addEventListener("click", onMyGroupClick);
function onMyGroupClick(event) {
console.log("click myGroup: " + event.currentTarget.id + ", " + event.target.id);
} myDiv.addEventListener("click", onMyDivClick);
function onMyDivClick(event) {
console.log("click myDiv: " + event.currentTarget.id + ", " + event.target.id);
}
</script>
</body>
</html>

点击Click me后打印如下:

click myDiv: myDiv, myDiv
click myGroup: myGroup, myDiv
click body: myBody, myDiv

取消默认行为

事件可以取消默认行为,什么是默认行为呢?比如一个设定了目标地址的a标签,默认点击后会打开对应的地址,这个不是我们添加的行为就是默认行为。我们可以通过event对象的cancelable来判断是否可以取消默认行为,为true表示可以取消,调用preventDefault方法就可取消默认行为,如下:

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<body>
<a id="myLink" href="http://www.baidu.com">link</a>
<script type="text/javascript">
var myLink = document.getElementById("myLink");
myLink.addEventListener("click", linkClickHandler); function linkClickHandler(event) {
if (event.cancelable)
event.preventDefault();
}
</script>
</body>
</html>

正常情况下点击link会进行跳转,加入取消默认事件的代码后点击link后就没有跳转网页的执行了。

取消事件冒泡

我们知道事件是会向根对象进行冒泡的,我们同样可以通过代码暂停事件继续进行冒泡,有两个方法可以使用:

  • stopPropagation:取消事件向父级冒泡;
  • stopImmediatePropagation:取消事件向父级及同级的冒泡;

这两个方法都是在bubbles属性为true时可以使用。

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<body>
<div id="myDiv">click me</div>
<script type="text/javascript">
var myDiv = document.getElementById("myDiv");
myDiv.addEventListener("click", divClickHandler);
myDiv.addEventListener("click", divClickHandler2); function divClickHandler(event) {
if (event.bubbles)
event.stopPropagation();
console.log("1");
} function divClickHandler2(event) {
console.log("2");
} document.body.addEventListener("click", bodyClickHandler); function bodyClickHandler(event) {
console.log("3");
}
</script>
</body>
</html>

调用stopPropagation后打印“1”和“2”,表示阻止了向父级的冒泡,但是同级的事件监听仍然可以接收事件。

修改代码如下后:

 function divClickHandler(event) {
if (event.bubbles)
event.stopImmediatePropagation();
console.log("1");
}

只会打印“1”表示阻止了后续的所有事件,包含同级及父级的事件。

事件类型

浏览器中,已经定义了许多事件相关的类型,大体如下:

http://www.w3school.com.cn/jsref/dom_obj_event.asp

内存和性能

这里讨论下提升性能的方法。

事件委托

在一些其它语言的GUI项目中,为每个按钮添加事件处理函数是很正常且也不会影响性能的,但是在JS中,每个函数都是对象,如果为每个点击项都额外添加一个处理函数,无疑会增加消耗,这时采用冒泡的特性在父级对象上添加一个函数,通过target属性的id来区分不同的点击项可以减少页面中的处理函数了。

及时移除不需要的事件监听

对于不使用了的事件监听,请及时移除,比如在div内的一个元素存在监听,在没有移除监听之前就更改了innerText导致移除了内部元素但是没有移除监听就有可能导致移除元素无法垃圾回收。

模拟事件

在JS中,我们还可以通过代码创建一个特定的事件进行播放来进行事件的模拟,下面我们来看看如何模拟一个系统的事件。

模拟鼠标事件

我们看看模拟鼠标事件的代码:

 <!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<body>
<a id="myLink" href="http://www.baidu.com">click me</a>
<script type="text/javascript">
setTimeout(function(){
var myLink = document.getElementById("myLink");
//创建鼠标事件
var evt = document.createEvent("MouseEvents");
//初始化鼠标事件
evt.initMouseEvent("click", true, true, document.defaultView);
//在目标对象上播放
myLink.dispatchEvent(evt);
}, 3000);
</script>
</body>
</html>

3秒之后会打开百度首页。

initMouseEvent的参数描述点击这里

模拟其它事件

注意createEvent的参数,使用“MouseEvents”创建鼠标事件,使用“HTMLEvents”可以创建页面事件,使用“MutationEvents”可以创建DOM的变动事件。具体的使用方法可以自行搜索。

自定义事件

同样的,我们也可以创建自定义的事件,如下:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<body>
<a id="myLink" href="http://www.baidu.com">click me</a>
<script type="text/javascript">
document.body.addEventListener("myEvent", function(event){
console.log("onMyEvent name: " + event.detail.name);
}); setTimeout(function(){
var myLink = document.getElementById("myLink");
//创建自定义事件
var evt = document.createEvent("CustomEvents");
//初始化自定义事件
evt.initCustomEvent("myEvent", true, true, {name:"Li Lei"});
//在目标对象上播放
myLink.dispatchEvent(evt);
}, 3000);
</script>
</body>
</html>

但是,CustomEvents并不是所有浏览器都支持的事件类型,比如Chrome就不支持,所以,当我们需要使用到跨浏览器的自定义事件时,一般还是使用观察者模式来实现,当然自己实现的话就不能拥有冒泡的功能了。

HTML5学习笔记(二十五):事件的更多相关文章

  1. python3.4学习笔记(二十五) Python 调用mysql redis实例代码

    python3.4学习笔记(二十五) Python 调用mysql redis实例代码 #coding: utf-8 __author__ = 'zdz8207' #python2.7 import ...

  2. Java基础学习笔记二十五 MySQL

    MySQL 在dos中操作mysql 连接mysql命令: mysql -uroot -p密码 ,连接OK,会出现mysql> 对数据库的操作 创建一个库 create database 库名 ...

  3. Java学习笔记二十五:Java面向对象的三大特性之多态

    Java面向对象的三大特性之多态 一:什么是多态: 多态是同一个行为具有多个不同表现形式或形态的能力. 多态就是同一个接口,使用不同的实例而执行不同操作. 多态性是对象多种表现形式的体现. 现实中,比 ...

  4. PHP学习笔记二十五【类的继承】

    <?php //定义父类 class Stu{ public $name; protected $age; protected $grade; private $address;//私有变量不会 ...

  5. HTML5学习笔记(十五):方法

    在一个对象中绑定函数,称为这个对象的方法. 在JavaScript中,对象的定义是这样的: var xiaoming = { name: '小明', birth: 1990 }; 但是,如果我们给xi ...

  6. angular学习笔记(二十五)-$http(3)-转换请求和响应格式

    本篇主要讲解$http(config)的config中的tranformRequest项和transformResponse项 1. transformRequest: $http({ transfo ...

  7. python3.4学习笔记(二十四) Python pycharm window安装redis MySQL-python相关方法

    python3.4学习笔记(二十四) Python pycharm window安装redis MySQL-python相关方法window安装redis,下载Redis的压缩包https://git ...

  8. 学习笔记:CentOS7学习之二十五:shell中色彩处理和awk使用技巧

    目录 学习笔记:CentOS7学习之二十五:shell中色彩处理和awk使用技巧 25.1 Shell中的色彩处理 25.2 awk基本应用 25.2.1 概念 25.2.2实例演示 25.3 awk ...

  9. Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装项目其它需要包 清除冗余文件并重新规划项目目录 配置文件 规划示例路由,并新建相关文件 实现数据访问和业务逻辑相关方法 编写mys ...

  10. [转]Nodejs学习笔记(十五)--- Node.js + Koa2 构建网站简单示例

    本文转自:https://www.cnblogs.com/zhongweiv/p/nodejs_koa2_webapp.html 目录 前言 搭建项目及其它准备工作 创建数据库 创建Koa2项目 安装 ...

随机推荐

  1. Redis从入门到精通:中级篇(转)

    原文链接:http://www.cnblogs.com/xrq730/p/8944539.html,转载请注明出处,谢谢 本文目录 上一篇文章以认识Redis为主,写了Redis系列的第一篇,现在开启 ...

  2. Qt Installer Framework 使用说明(一)

    目录 Qt Installer Framework 使用说明 1.Qt Installer Framework概述 选择安装包类型 促进更新 提供安装内容 2.入门指南 支持的平台 从源代码构建 支持 ...

  3. PHP 反射API说明

    2.API概览:class Reflection { }interface Reflector { }class ReflectionException extends Exception { }cl ...

  4. WIN10-修改网卡MAC

    在注册表位置: 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002 ...

  5. win10更新后无法远程,报 credssp加密oracle修正

    答案都在图里,看不清就浏览器放大观看 打开开始菜单,搜索“编辑组策略”  进入

  6. win7 远程桌面连接centos 6.5

    首先安装tigervnc-server: yum install tigervnc-server 安装好后,设置 vi /etc/sysconfig/vncservers 'man vncpasswd ...

  7. weblogic部署存在中文乱码导致部署失败

    问题描述: weblogic控制台显示错误,是受管节点console日志 <-- 下午10时19分16秒 CST> <Info> <Security> <BE ...

  8. 常用Raspberry Pi周边传感器的使用教程(转)

    转:http://bbs.xiaomi.cn/thread-7797152-1-1.html 在Raspberry Pi 的使用和开发过程中,你可能时常需要一些硬件和传感器等来支持你的开发工作,例如, ...

  9. Python的多线程技巧

    使用多线程可以加快程序运行速度. 遍历占用cpu不高,单线程跑程序占用的CPU很少. 多线程则是CPU占用多,内存占用少了. [一个实例]:multi_process.py ### multi_pro ...

  10. __cplusplus的用法(转)

    经常在/usr/include目录下看到这种字句: #ifdef __cplusplusextern "C" {#endif...#ifdef __cplusplus}#endif ...