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

事件流

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

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

 <!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. 基于python2【重要】怎么自行搭建简单的web服务器

    基本流程:1.需要的支持     1)python本身有SimpleHTTPServer     2)ForkStaticServer.py支持,该文件放在python7目录下     3)将希望共享 ...

  2. Intellij Idea 导入多个maven项目展示在左侧栏Maven Projects

    刚刚要开始从eclipse切换成idea,据说idea功能强大,可是刚刚开始使用很多不习惯,导入第二个maven项目时之前的项目就没了,比较苦恼,下面介绍下导入多个maven项目展示在左侧栏Maven ...

  3. windows下卸载mysql5.5,升级为mysql5.7.25

    0. 停止mysql的服务 1. 卸载mysql5.5 1.1 使用360或者控制面板卸载mysql,为求干净,我使用的360,结果也需要手动清理 1.2 卸载完成之后到mysql的安装目录删掉该目录 ...

  4. Javascript中类型的判断

    数据类型的判断有这么几种方式 1.一元运算符 typeOf 2.关系运算符 instanceof 3.constructor 属性 4.prototype属性 一.typeof typeof的返回值有 ...

  5. iOS UILabel设置行间距和字间距

    实现UILabel的文字,设置行间距和字间距. 效果图: 代码: let lblTitle = UILabel(frame: CGRect(x: , y: , width: KScreenWidth- ...

  6. Ubuntu中root用户和user用户的相互切换[转载自93度的饼干]

    Ubuntu中root用户和user用户的相互切换 Ubuntu是最近很流行的一款Linux系统,因为Ubuntu默认是不启动root用户,现在介绍如何进入root的方法. (1)从user用户切换到 ...

  7. nginx配置文件结构,语法,配置命令解释

    摘要: nginx的配置文件类似于一门优雅的编程语言,弄懂了它的规范就可以自定义配置文件了,这个很重要~ 1,结构分析 nginx配置文件中主要包括六块:main,events,http,server ...

  8. Nginx+FastCGI运行原理(一)

    1 实战Nginx与PHP(FastCGI)的安装.配置与优化 1.1 什么是 FastCGI FastCGI是一个可伸缩地.高速地在HTTP server和动态脚本语言间通信的接口.多数流行的HTT ...

  9. 进阶之路(基础篇) - 009 通过底层AVR方法实现SPI数据传输

    主机端: /********************************* 代码功能:通过底层AVR方法实现SPI数据传输(主机端) 创作时间:2016*10*17 使用资源: 更低阶的 aTme ...

  10. 【Android】Android动态加载Jar、APK的实现

    本文介绍Android中动态加载Jar.APK的实现.而主要用到的就是DexClassLoader这个类.大家都知道Android和普通的Java虚拟机有差别,它只能加载经过处理的dex文件.而加载这 ...