事件绑定的方式

给 DOM 元素绑定事件分为两大类:在 html 中直接绑定 和 在 JavaScript 中绑定。

Bind in HTML

在 HTML 中绑定事件叫做内联绑定事件,HTML 的元素中有如 onclick 这样的 on*** 属性,它可以给这个 DOM 元素绑定一个类型的事件,主要是这样的:

<div onclick="***">CLICK ME</div>

这里的 *** 有两种形式:

  • 用字符串表示一段函数:
<div onclick="var a = 1; console.log(a); console.log(this);">CLICK ME</div>

点击可以发现,console:

1
<div onclick=​"var a = 1;​ console.log(a)​;​ console.log(this)​;​">​CLICK ME​</div>​

var a = 1; console.log(a); console.log(this); 这一段字符串被当作 js 执行了,同时 this 指向当前这个点击的元素。

  • 用函数名表示:
<div onclick="foo(this)">CLICK ME</div>

这里需要添加 script 去定义函数:

<script>
function foo(_this) {
console.log(_this);
console.log(this);
}
</script>

可以观察到,console:

<div onclick=​"var a = 1;​ console.log(a)​;​ console.log(this)​;​">​CLICK ME​</div>​
window

这里的 this 指向了全局,传入的参数是点击的 DOM 元素,其实和第一种方式是一样的,都是在 onclick= 后面指向了一段js的字符串,不同的是在这个字符串中是执行了一个函数名,而这个函数我们在全局中定义了,所以点击的时候可以执行,然后传入的参数 this 也就是一样的道理了。

或者多说一句,这里的字符串才是真正赋值给 onclick 的函数,这里我们是在函数里面再执行了 foo 函数。

然而这内联的方式绑定时间不利于分离,所以一般我们不推荐这种做法,所以也就不再多阐述了

Bind in JavaScript

dom.onclick

先上栗子:

<div id="clickme">CLICK ME</div>
document.getElementById('clickme').onclick = function (e) {
console.log(this.id);
console.log(e);
};

观察 console:

clickme
MouseEvent {isTrusted: true, screenX: 65, screenY: 87, clientX: 65, clientY: 13…}

首先,我们获取到了 dom 元素,然后给它的 onclick 属性赋值了一个函数;

点击 dom 我们发现那个函数执行了,同时发现函数中的 this 是指向当前的这个 dom 元素;

细细一想,其实这和前面用的在 html 中 内联 绑定函数是一样的,我们同样是给 dom 的 onclick 属性赋值一个函数,然后函数中的 this 指向当前元素,只是这个过程这里我们是在 js 中做的;

而还有一点区别就是前面的是赋值一段 js 字符串,这里是赋值一个函数,所以可以接受一个参数:event,这个参数是点击的事件对象。

用赋值绑定函数的一个缺点就是它只能绑定一次:

document.getElementById('clickme').onclick = function (e) {
console.log(this.id);
};
document.getElementById('clickme').onclick = function (e) {
console.log(this.id);
};

可以看到这里只打印了一次 clickme

addEventListener

这个才是我们需要重点介绍的一个函数

语法

target.addEventListener(type, listener[, useCapture]);
  • target : 表示要监听事件的目标对象,可以是一个文档上的元素 Document 本身,Window 或者 XMLHttpRequest
  • type : 表示事件类型的字符串,比如: clickchangetouchstart …;
  • listener : 当指定的事件类型发生时被通知到的一个对象。该参数必是实现 EventListener 接口的一个对象或函数。
  • useCapture : 设置事件的 捕获或者冒泡 (后文阐述) ,true: 捕获,false: 冒泡*,默认为 false

简单栗子

<div id="clickme">CLICK ME</div>
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1);

function foo1(event) {
console.log(this.id);
console.log(event);
}

观察 console:

clickme
MouseEvent {isTrusted: true, screenX: 37, screenY: 88, clientX: 37, clientY: 14…}

监听函数中的 this 指向当前的 dom 元素,函数接受一个 event 参数。

绑定函数

绑定多个函数

addEventListener 可以给同一个 dom 元素绑定多个函数:

<div id="clickme">CLICK ME</div>
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1);
clickme.addEventListener('click', foo2); function foo1(event) {
console.log(111);
} function foo1(event) {
console.log(222);
}

可以看到 console:

111
222

我们可以看到两个函数都执行了,并且执行顺序按照绑定的顺序执行。

改变一下,如果我们的 useCapture 参数不同呢?
看下面 3 组对比:

1
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1, true);
clickme.addEventListener('click', foo2, true); function foo1(event) {
console.log(111);
} function foo1(event) {
console.log(222);
}
2
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1, true);
clickme.addEventListener('click', foo2, false); function foo1(event) {
console.log(111);
} function foo1(event) {
console.log(222);
}
3
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1, false);
clickme.addEventListener('click', foo2, true); function foo1(event) {
console.log(111);
} function foo1(event) {
console.log(222);
}

可以看到,console 并没有改变,所以执行顺序只和绑定顺序有关,和 useCapture 无关。

结论:
我们可以给一个 dom 元素绑定多个函数,并且它的执行顺序按照绑定的顺序执行。

同一个元素绑定同一个函数

我们给一个 dom 元素绑定同一个函数两次试试:

<div id="clickme">CLICK ME</div>
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1);
clickme.addEventListener('click', foo1); function foo1(event) {
console.log(111);
}

观察 console:

111

可以看到函数只执行了一次;

换一种方式:

var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1, true);
clickme.addEventListener('click', foo1, false); function foo1(event) {
console.log(111);
}

观察 console:

111
111

可以看到函数执行了两次。

结论:
我们可以给一个 dom 元素绑定同一个函数,最多只能绑定 useCapture 类型不同的两次。

IE下使用attachEvent/detachEvent

addEventListener 只支持到 IE 9,所以为了兼容性考虑,在兼容 IE 8 以及以下浏览器可以用 attachEvent 函数,和 addEventListener 函数表现一样,除了它绑定函数的 this 会指向全局这个缺点以外

事件执行顺序的 PK

1. addEventListener 和 dom.onclick

<div id="clickme">CLICK ME</div>
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1, true);
clickme.onclick = foo2;
clickme.addEventListener('click', foo3, true); function foo1(event) {
console.log(111);
} function foo2(event) {
console.log(222);
} function foo3(event) {
console.log(333);
}

观察 console:

111
222
333

可见执行顺序只和绑定顺序有关

2. addEventListener 间的比较

见上文

事件的解绑

与事件绑定相对应的就是事件解绑了。

1. 通过 dom 的 on** 属性设置的事件

对于 Bind in HTML 和 dom.onclick 绑定的事件都可以用 dom.onclick = null 来解绑事件。

2. removeEventListener

通过 addEventListener 绑定的事件可以使用 removeEventListener 来解绑, removeEventListener 接受的参数和 addEventListener 是一样的

<div id="clickme">CLICK ME</div>
var clickme = document.getElementById('clickme');

clickme.addEventListener('click', foo1);

function foo1(event) {
console.log(111);
}
clickme.removeEventListener('click', foo1, true);

这里发现事件并没有取消绑定,发现 removeEventListener 的 useCapture 的参数原来和绑定时候传入的不一致,我们改成 false 之后发现事件取消了。

结论:
对于使用 removeEventListener 函数解绑事件,需要传入的 listener useCapture 应和 addEventListener 一致才可以解绑事件。

3. detachEvent

与 attachEvent 对应

DOM 事件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div class="div1">
<div class="div2">
<div class="div3"> </div>
</div>
</div>
</body>
</html>

事件冒泡

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

比如上面的 HTML ,冒泡的顺序: div3 -> div3 -> div1 -> body -> html -> document (-> window)

事件捕获

事件捕获的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件。与事件冒泡的顺序相反。

比如上面的 HTML ,捕获的顺序: document -> html -> body -> div1 -> div2 -> div3

DOM事件流

DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。然后是实际的目标接受事件。最后一个阶段是时间冒泡阶段,可以在这个阶段对事件做出响应。

回到 addEventListener

我们来做几个小对比:

<div id="wrap">
<div id="clickme">CLICK ME</div>
</div>

栗子 1

document.getElementById('wrap').addEventListener('click', foo1, false);
document.getElementById('clickme').addEventListener('click', foo2, false); function foo1(event) {
console.log(111);
}
function foo2(event) {
console.log(222);
}

console:

222
111

这里两个事件都是冒泡类型,所以是从内到外;

栗子 2

document.getElementById('wrap').addEventListener('click', foo1, true);
document.getElementById('clickme').addEventListener('click', foo2, true); function foo1(event) {
console.log(111);
}
function foo2(event) {
console.log(222);
}

console:

111
222

这里两个事件都是捕获类型,所以是从外到内;

栗子 3

document.getElementById('wrap').addEventListener('click', foo1, false);
document.getElementById('clickme').addEventListener('click', foo2, true); function foo1(event) {
console.log(111);
}
function foo2(event) {
console.log(222);
}

console:

222
111

wrap 事件是冒泡,clickme 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。

栗子 4

document.getElementById('wrap').addEventListener('click', foo1, true);
document.getElementById('clickme').addEventListener('click', foo2, false); function foo1(event) {
console.log(111);
}
function foo2(event) {
console.log(222);
}

console:

111
222

clickme 事件是冒泡,wrap 事件是捕获,根据 dom 的事件流,先执行了捕获阶段(这里是目标阶段)再到冒泡阶段。

阻止 冒泡/捕获

<div id="wrap">
<div id="clickme">CLICK ME</div>
</div>
document.getElementById('wrap').addEventListener('click', foo1);
document.getElementById('clickme').addEventListener('click', foo2); function foo1(event) {
console.log(111);
}
function foo2(event) {
event.stopPropagation();
console.log(222);
}

从一个事件绑定说起 - DOM的更多相关文章

  1. Javascript 事件对象(四)一个事件绑定多个不同的函数

    给一个对象绑定多个事件处理函数: <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-T ...

  2. vue中把一个事件绑定到子组件上

    官网上是这样描述的 你可能有很多次想要在一个组件的根元素上直接监听一个原生事件.这时,你可以使用 v-on的 .native 修饰符 父组件App.vue <template> <d ...

  3. jQuery中事件绑定到bind、live、delegate、on方法的探究

    1. 给页面上的某个元素绑定事件,最初采用下面的方式实现: $(‘selector’).click(function(){ //code }); 缺点: 不能同时绑定多个事件,不能绑定动态的元素. 后 ...

  4. dojo事件驱动编程之事件绑定

    什么是事件驱动? 事件驱动编程是以事件为第一驱动的编程模型,模块被动等待通知(notification),行为取决于外来的突发事件,是事件驱动的,符合事件驱动式编程(Event-Driven Prog ...

  5. jQuery 事件绑定 和 JavaScript 原生事件绑定

    总结一下:jQuery 事件绑定 和 JavaScript 原生事件绑定 及 区别 jQuery 事件绑定 jQuery 中提供了四种事件监听绑定方式,分别是 bind.live.delegate.o ...

  6. js事件绑定的几种方式

    在JavaScript中,有三种常用的绑定事件的方法: 在DOM元素中直接绑定: 在JavaScript代码中绑定: 绑定事件监听函数 一. 在DOM元素中直接绑定 这里的DOM元素,可以理解为HTM ...

  7. 事件绑定addEventListener

    通过addEventListener监听函数实现的dom事件绑定 addEventListener可以为当前dom添加一个事件(这个事件可以是个已有的事件),这就无法避免我们在写代码的时候重复去绑定同 ...

  8. 原生JS事件绑定方法以及jQuery绑定事件方法bind、live、on、delegate的区别

    一.原生JS事件绑定方法: 1.通过HTML属性进行事件处理函数的绑定如: <a href="#" onclick="f()"> 2.通过JavaS ...

  9. Dojo初探之5:dojo的request(请求)操作、请求过程事件绑定和隐藏数据data()操作(基于dojo1.11.2版本)

    前言: 上一章详细阐述了dojo的事件绑定操作,本章将讲解dojo的请求操作 注:dojo的请求操作与js和jquery完全不同! 1.dojo的请求 dojo通过request.get()/.put ...

随机推荐

  1. 在Debian系列Linux系统Ubuntu上安装配置yum的试验

    用习惯了Red Hat系统的都知道我们习惯于三种安装方式:一种是rpm包的方式安装,一种就是tar包的方式来安装,还有一种方式就是yum源的安装. 首先rpm包的用法,我们一般是在Red Hat光驱里 ...

  2. unity(c# ioc框架) 使用总结

    这里的unity指的是完成依赖注入的unity而不是游戏引擎. 原本项目完成依赖注入用的是spring.net,但是spring.net已经很久没人维护了,所以微软官方推出的Unity成为了替代spr ...

  3. vue-cli工具搭建vue-webpack项目

    1.安装node环境 下载地址 https://nodejs.org/en/download/ node -v   安装成功后在命令行查看node版本 npm-v   安装成功后在命令行查看npm版本 ...

  4. IP地址与域名的关系

    1.IP地址:IP地址是用来唯一标识互联网上计算机的逻辑地址,让电脑之间可以相互通信. 每台连网计算机都依靠IP地址来互相区分.相互联系 2.域名:由于IP地址是数字标识,使用时难以记忆和书写,因此在 ...

  5. wcf感悟与问题

    默认情况下,BasicHttpBinding发送的是明文数据,而WsHttpBinding发送的是加密和更加安全的数据.契约相当于公司与客户之间签订的合同DataContract需要引用命名空间sys ...

  6. Centos7数据实时同步

    Rsync+inotify 功能要求 通过rsync+inotify将数据库指定目录实时同步到备份服务器. 环境说明 M:192.168.10.11 数据库服务器 S:192.168.10.13 备份 ...

  7. Web开发中Listener、Filter、Servlet的初始化及调用

    我们在使用Spring+SpringMVC开发项目中,web.xml中一般的配置如下: <?xml version="1.0" encoding="UTF-8&qu ...

  8. 二分查找的变种(Java实现)

    普通二分查找: 先回顾一下普通的二分查找 注意:二分查找有这样一个问题:当数组中数有重复时,比如 {3,3,3,3} 这个数组,二分查找3时,返回的是arr[1],也就是说二分查找并不会返回3第一次出 ...

  9. 剑指Offer-平衡二叉树

    package Tree; /** * 平衡二叉树 * 输入一棵二叉树,判断该二叉树是否是平衡二叉树. * 平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法), ...

  10. springboot集成swagger2

    介绍:        Swagger是全球最大的OpenAPI规范(OAS)API开发工具框架,支持从设计和文档到测试和部署的整个API生命周期的开发.(摘自Swagger官网)Swagger说白了就 ...