读书笔记(05) - 事件 - JavaScript高级程序设计
HTML依托于JavaScript来实现用户与WEB网页之间的动态交互,接收用户操作并做出相应的反馈,而事件在此间则充当桥梁的重要角色。
日常开发中,经常会为某个元素绑定一个事件,编写相应的业务逻辑,在元素被点击时执行,并反馈到用户操作界面。
这个过程中,事件就像一个侦听器,当点击动作发生时,才会执行对应的程序。这种模式可称之为观察员模式。
接下来就讲讲DOM事件相关知识。
何为事件
事件就是用户或浏览器自身执行的某种动作
常用的DOM事件有click/mouseover/mouseout/keyup/keydown
等。
事件流
事件流描述的是从页面中接收事件的顺序
HTML描述的是一个DOM文档结构,而事件流所描述的是DOM文档节点接收事件顺序。
而事件流有两种事件模式,捕获/冒泡,两者所描述的事件传递顺序对立相反。
事件模式:捕获与冒泡
冒泡
事件冒泡:事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)
规范要求事件冒泡到document
对象,而浏览器则会将事件一直冒泡到window
对象。
所有浏览器都支持事件冒泡(包括IE9以下)。
捕获
事件捕获:(与事件冒泡相反)事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件
与冒泡一样,虽然规定事件应该从document
对象开始传播,但浏览器普遍都是从window
对象开始捕获。
IE9以下不支持事件捕获
DOM 事件流
"DOM2级事件"规定事件流包括三个阶段,顺序进行
事件捕获阶段
处于目标阶段
事件冒泡阶段
TIPS: 实际的目标元素在捕获阶段不会接收到事件,在处于目标阶段时接收事件发生处理,并被看成是冒泡阶段的一部分。
尽管"DOM2级事件"规范明确要求捕获阶段不会涉及事件目标,但浏览器会在捕获阶段触发事件对象上的事件。
事件处理程序
响应某个事件的函数方法,我们称之为事件处理程序(或事件侦听器)
window.onclick = function() {
//...
}
// 这里的function(){}就是事件处理程序
HTML事件处理程序
HTML中元素支持的事件,可以使用一个同名的HTML特性来指定,而这个特性的值就是js能执行的代码或表达式。写法上可以看出类似HTML中id/type/class
等属性的写法,都是on+'...'
缺点:HTML是结构层(显示层),而JavaScript是行为层(业务层)。在显示层上去编写业务逻辑代码处理,会使得HTML与JavaScript代码耦合过于紧密,不好维护。
DOM级别一共可以分为四个级别:DOM0级、DOM1级、DOM2级和DOM3级。
而DOM事件分为3个级别:DOM 0级事件处理程序,DOM 2级事件处理程序和DOM 3级事件处理程序。DOM 1级中没有规范事件的相关内容,所以没有DOM 1级事件处理。
DOM0 级事件处理程序
每个元素(HTML元素)都有自己的事件处理程序属性,属性名通常以on开头,例如onclick/onmouseover
。为这个属性的值设置一个函数,就可以指定事件处理程序。而将其属性值赋值为null,则完成解绑。(同个元素无法绑定多个同名事件)
var myBtn = document.getElementById('myBtn');
// 为myBtn绑定事件处理程序, 只能绑定一个
myBtn.onclick = function() {
alert('Hello world!');
}
// 解绑
myBtn.onclick = null;
DOM2 级事件处理程序
"DOM2级事件"定义了两个方法,addEventListener()/removeEventListener()
,用于为元素绑定和解绑事件。
(可绑定多个事件,区别于DOM0级/HTML仅能绑定一个)。
el.addEventListener(eventName, callBack, useCapture)
eventName: 事件名称
callBack: 回调函数,当事件触发时,函数会传入一个参数event,为当前的事件对象
useCapture: 默认是false,代表事件句柄在冒泡阶段执行, true则代表在捕获阶段执行
var myBtn = document.getElementById('myBtn');
var handleClick = function() {
alert('Hello world!');
}
// 绑定事件处理程序
myBtn.addEventListener('click', handleClick, false);
// 解绑
myBtn.removeEventListener('click', handleClick);
TIPS:DOM2级事件处理程序,解绑时function
必须与传入addEventListener
相同
// 绑定
myBtn.addEventListener('click', function() {
// 匿名函数
});
// 解绑
myBtn.removeEventListener('click',function() {
// 匿名函数
});
// add/remove 分别绑定了两个匿名函数(函数为引用类型),所以两个函数并不相同,所以无法成功解绑
TIPS:绑定多个事件处理程序时,执行顺序按绑定顺序执行
myBtn.addEventListener('click', function() {
// step1...
})
myBtn.addEventListener('click', function() {
// step2...
})
// 执行顺序:step1 -> step2
浏览器支持情况:IE9以下不支持DOM2级事件处理程序
IE 事件处理程序
IE9以下不支持DOM2级事件,但IE提供了与DOM2级事件类似的两个方法,attachEvent()/detachEvent
,IE9以下不支持事件捕获,所以attachEvent
仅支持冒泡阶段触发,只接收两个参数(eventName, function)。
// 绑定
myBtn.attachEvent('onclick', handleClick);
// 解绑
myBtn.detachEvent('onclick', handleClick);
TIPS:
解绑时
function
必须与传入attachEvent
相同,这点与DOM2级事件相同与DOM0级的区别,DOM0级事件处理在元素的作用域运行,而
attachEvent
事件处理在全局,this
指向window
绑定多个事件处理程序时,执行顺序按绑定顺序逆反执行(与DOM2级相反)
myBtn.attachEvent('click', function() {
// step1...
})
myBtn.attachEvent('click', function() {
// step2...
})
// 执行顺序:step2 -> step1
Event 事件对象
常见应用
event.preventDefault()
阻止默认事件
event.stopPropagation()
阻止事件流发生传递(冒泡/捕获)
event.stopImmediatePropagation()
阻止剩余事件处理函数的执行,并阻止当前事件在事件流上传递
event.currentTarget
当前绑定事件的元素
event.target
当前触发事件的元素
event.stopPropagation()与.stopImmediatePropagation()的区别
同个元素绑定多个同名事件时,stopImmediatePropagation
不仅阻止了冒泡,而且会阻止后续事件的执行,可以理解为加强版的stopPropagation
myBtn.addEventListener('click', function(event) {
// step1;
event.stopImmediatePropagation();
})
myBtn.addEventListener('click', function(event) {
// step2;
// 我被stopImmediatePropagation阻止掉了!!!
})
currantTarget与target的区别
事件处理程序内部,this等于currentTarget(当前绑定事件的元素),而target(当前触发事件的元素)
// currentTarget == target
myBtn.addEventListener('click', function(event) {
event.target == event.currentTarget; // true -> myBtn
})
// currentTarget != target 捕获/冒泡
document.body.addEventListener('click', function(event){
event.target == event.currentTarget; // false
// event.target -> myBtn
// event.currentTarget -> body
})
内存与性能
WEB网页是运行在浏览器客户端的,而计算机分配给浏览器的内存及CPU占用是有限制的。虽说浏览器引擎不断地发展优化,但是内存占用多了, 性能不免会损耗。
内存
为元素指定事件绑定程序,事实上是赋值了一个函数方法,而函数在javaScript中是一种引用类型的数据格式,既然是数据那就需要用到内存储存。函数创建多了,消耗掉内存。
性能
为元素指定事件绑定程序,首先需要对DOM进行查询,找出要绑定事件的元素。而这也会造成DOM元素的访问次数增加。DOM的操作一直是网页性能的一个优化点。
了解完事件绑定带来内存跟性能的原理,我们来看一个例子,例如我们有一个ul>li
的列表,要监听每一个li
的点击事件,并触发事件处理程序。
单独绑定的话,10个li
就要对DOM元素查询10次,创建的匿名函数就有10个(当然可以共同创建同个函数引用),如果还有20个,30个,100个,那么这种为每个li
元素单独绑定事件的方法,绝对不是最优解。
这就引出下面的优化方案:"事件委托"。
事件委托(事件代理)
对"事件处理程序绑定过多"的问题,最好的解决方案就是"事件委托"。它的原理是利用了事件流的"冒泡"机制,事件目标元素会把事件向上层传递,直到document
(浏览器会传到window
),所以父级节点是可以接收子节点的事件传递。
以刚刚ul>li
的例子,li
有很多个, 但它们有一个共同的父节点ul
。li
的点击事件会冒泡到ul
,因此我们可以在ul
上绑定一个事件处理程序,处理所有li
的点击事件,然后通过event.target
可以确定触发事件的元素。
var ulParent = document.getElementById('parent');
ulParent.addEventListener('click', function(event) {
var taget = event.target;
})
通过"事件委托"减少了DOM元素的查询,以及多个函数的内存占用,而且还有一个好处,当我们的li
是动态的,增加和移除时,都无需再做绑定和解绑事件操作,因为它都会冒泡到父级节点。
移除多余的事件绑定
文档中移除了绑定了事件的DOM元素,如innerHTML/removeChild()/replaceChild()
等可以对DOM进行替换,而移除的DOM元素原先所绑定的事件处理程序,并不能有效被浏览器垃圾回收,所以占用一直存在。
所以建议在移除某个DOM元素时,如果其绑定了事件处理程序,需手动解除绑定,释放内存。
自定义事件
除了为元素绑定支持的事件以外,我们还可以通过Event/CustomEvent
来创建开发者自定义事件。
两者不同的是CustomEvent
可传递一个Object
对象来传输数据。
// Event
var eve = new Event('custome');
// CustomeEvent 可传参数
var eve = new CustomeEvent('custome', {
detail: {
name: 'KenTsang',
age: 28
}
});
// 为DOM元素添加事件监听
ele.addEventListener('custome', function(event) {
console.log(event.detail);
})
// 触发ele绑定的自定义事件
ele.dispatch(eve);
事件这块还剩下一部分知识点,后续文章会再就模拟事件这块知识点进行拆分详解。
天冷了,更文不易,望大家多多点赞。
作者:以乐之名本文原创,有不当的地方欢迎指出。转载请指明出处。
读书笔记(05) - 事件 - JavaScript高级程序设计的更多相关文章
- 读书笔记(03) - 性能 - JavaScript高级程序设计
作用域链查找 作用域链的查找是逐层向上查找.查找的层次越多,速度越慢.随着硬件性能的提升和浏览器引擎的优化,这个慢我们基本可以忽略. 除了层级查找损耗的问题,变量的修改应只在局部环境进行,尽量避免在局 ...
- 读书笔记(02) - 可维护性 - JavaScript高级程序设计
编写可维护性代码 可维护的代码遵循原则: 可理解性 (方便他人理解) 直观性 (一眼明了) 可适应性 (数据变化无需重写方法) 可扩展性 (应对未来需求扩展,要求较高) 可调试性 (错误处理方便定位) ...
- 读书笔记(01) - JSON - JavaScript高级程序设计
JSON与JavaScript对象 JSON是一种表示结构化数据的存储格式,语法格式上与JavasScript对象有些类似. TIPS: 与JavaScript对象的格式区别 不支持变量.函数或对象实 ...
- 学习笔记:《JavaScript高级程序设计》
第1章 JavaScript简介 1.一个完整的JavaScript实现应该由三部分组成:核心(ECMAScript),文档对象模型(DOM)和浏览器对象模型(BOM). 2.Web浏览器只是ECMA ...
- 《JavaScript高级程序设计》读书笔记--前言
起因 web编程过程使用javascript时感觉很吃力,效率很低.根本原因在于对javascript整个知识体系不熟,看来需要找些书脑补一下,同时欢迎众网友监督. 大神推荐书籍 看了博客大神们推荐的 ...
- 《Javascript高级程序设计》读书笔记之对象创建
<javascript高级程序设计>读过有两遍了,有些重要内容总是会忘记,写一下读书笔记备忘 创建对象 工厂模式 工厂模式优点:有了封装的概念,解决了创建多个相似对象的问题 缺点:没有解决 ...
- JavaScript高级程序设计(读书笔记)(一)
本笔记汇总了作者认为“JavaScript高级程序设计”这本书的前七章知识重点,仅供参考. 第一章 JavaScript简介 JavaScript发展简史: 1995年,JavaScript诞生 19 ...
- 读书笔记(06) - 语法基础 - JavaScript高级程序设计
写在开头 本篇是小红书笔记的第六篇,也许你会奇怪第六篇笔记才写语法基础,笔者是不是穿越了. 答案当然是没有,笔者在此分享自己的阅读心得,不少人翻书都是从头开始,结果永远就只在前几章. 对此,笔者换了随 ...
- 读书笔记(04) - 错误监控 - JavaScript高级程序设计
错误类型 即时运行错误 (代码错误) 资源加载错误 常见的错误 1. 类型转换错误 建议使用全等===操作符 2.数据类型错误 建议加强类型判断 // 数组倒序 function reverseSor ...
随机推荐
- poj 2506 Tiling(高精度)
Description In how many ways can you tile a 2xn rectangle by 2x1 or 2x2 tiles? Here is a sample tili ...
- 初识Android的ReactiveX
初识Android的ReactiveX 开发一个复杂一点的Android应用都会用到网络请求,交互和动画.这些都意味着 要写很多的回调嵌套.这样的代码也被称为callback hell(回调地狱).这 ...
- 中国剩余定理 (POJ 1006)
http://poj.org/problem?id=1006 在<孙子算经>中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩 ...
- 修改linux swap空间的swappiness,降低对硬盘的缓存
linux 会使用硬盘的一部分做为SWAP分区,用来进行进程调度--进程是正在运行的程序--把当前不用的进程调成‘等待(standby)‘,甚至‘睡眠(sleep)’,一旦要用,再调成‘活动(acti ...
- 认识Hadoop
概述 开源.分布式存储.分布式计算 大数据生态体系 特点:开源.社区活跃 囊括了大数据处理的方方面面 成熟的生态圈 推荐系统 应用场景 搭建大型数据仓库,PB级数据的存储.处理.分析.统计 日志分析 ...
- 论EFMS模拟量部分采集电路的修改
论1:电阻R11的作用 如图1是2014-3-11之前模拟量采集的部分硬件电路,图2是纠正后的正确电路. D5是SA20CA,TVS双向二极管,有效防止外接电源的浪涌冲击情况,保护电路. D6是稳压 ...
- string转Date转回String(JAVA)
String dateString = "2012-12-06 "; SimpleDateFormat sdf = new SimpleDateFormat("yyy ...
- 个人作业四--Alpha阶段个人总结
一.个人总结 在alpha 结束之后, 每位同学写一篇个人博客, 总结自己的alpha 过程: 请用自我评价表:http://www.cnblogs.com/xinz/p/3852177.html 有 ...
- linux下java版本管理工具jenv使用介绍
不同的项目使用的java版本不同,每次切换时都需要手动去修改java的环境变量,麻烦至极. jenv可以管理java版本,轻松实现管理多个java的问题. 一.下载jenv $ git clone h ...
- 4.翻译:EF基础系列--EF架构
原文地址:http://www.entityframeworktutorial.net/EntityFramework-Architecture.aspx 下面的图形,展示了EF的总体架构: 让我们来 ...