JavaScript DOM 事件模型
JavaScript DOM 事件模型
JavaScript 是基于面向对象和事件驱动的一门语言,事件模型是 DOM 中至关重要的内容,理解事件驱动机制、事件反馈、事件冒泡、事件捕获以及事件委托能帮助我们更好的处理事件,写出更优的代码
事件驱动机制
- 当事件发生时,我们收到事件的反馈,在 JavaScript 中,事件反馈是我们自行定义的事件处理函数
- 事件,如点击事件、鼠标移入事件等,是每一个元素与生俱来的能力
- 通常说的绑定事件,实际上是绑定事件的反馈,即事件处理函数
- 例如点击一个按钮,按钮元素对象是事件发送器或事件源,事件是鼠标点击事件,事件处理函数是侦听器
- 元素对象发出事件,事件处理函数做出反应,这就是 JS 的事件驱动机制
在观察者模式中,事件发送器就是主题,事件处理函数即侦听器就是观察者
绑定事件反馈
内联属性
<button onclick="test()">按钮</button>
介于结构和逻辑要相分离,不建议使用内联方式绑定
事件句柄
var oBtn = document.getElementsByTagName('button')[0];
oBtn.onclick = function() {
// this -> oBtn
}
兼容性好,但是重复绑定会覆盖
事件监听器
var oBtn = document.getElementsByTagName('button')[0];
oBtn.addEventListener("click", funtion(){
// this -> oBtn
}, false);
oBtn.addEventListener("click", test, false);
funtion test(){
// 事件处理函数
}
重复添加,不会覆盖之前添加的监听器,但是如果事件类型、事件处理函数和最后一个布尔参数都相同,则不会重复执行
IE8 及以下不支持 addEventListener,可用 attachEvent 代替
var oBtn = document.getElementsByTagName('button')[0];
oBtn.attachEvent("onclick", funtion(){
// this -> window
});
// 区别于 addEventListener,第一个参数使用 'onclick',而不是 'click'
// 并且内部 this 指向 window
// 对于 attachEvent,如果事件类型、事件处理函数都相同,还是会重复执行
兼容性封装
function addEvent(elem, type, fn) {
if (elem.addEventListener) {
elem.addEventListener(type, fn, false);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, function(ev) {
fn.call(elem, ev); // call 兼容性比 bind 好
});
} else {
elem['on' + type] = fn;
}
}
解除绑定
oBtn.onclik = null;
oBtn.removeEventListener("click", test, false); // 解除 addEventListener
oBtn.detachEvent('onclick', test); // 解除 attachEvent
示例:点击一次后清除事件反馈
oBtn.onclik = function() {
// ...
this.onclick = null;
}
// 非严格模式
oBtn.addEventListener("click", funtion() {
// ...
this.removEventListener('cilck', arguments.callee, false);
}, false);
// 严格模式
oBtn.addEventListener("click", funtion temp() {
// ...
this.removeEventListener('click', temp, false);
}, false);
事件冒泡和捕获
事件冒泡:当一个元素发生事件时,该事件会向父级元素传递,按由子到父的顺序触发一连串的事件反馈,称之为事件冒泡
DOM 上的嵌套关系会产生事件冒泡,例如两个 div 嵌套,点击内部的 div,触发内部 div 的点击事件,内部 div 的点击事件处理函数进行响应,这个事件向其父级即外部 div 传递,外部 div 也有点击事件,外部 div 所绑定的点击事件反馈也会响应
<div class="outer">
<div class="inner"></div>
</div>
var outer = document.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];
outer.addEventListener('click', function () {
console.log('bubble outer');
}, false);
inner.addEventListener('click', function () {
console.log('bubble inner');
}, false);
// addEventListener 最后一个参数默认值为 false,表示事件冒泡
// 点击 inner,打印出
// bubble inner
// bubble outer
事件捕获:当一个元素发生事件时,该事件会向父级元素传递,按由父到子的顺序触发一连串的事件反馈,称之为事件捕获
事件捕获与事件冒泡的触发顺序相反,同样需要 DOM 上的嵌套关系
outer.addEventListener('click', function () {
console.log('outer');
}, true);
inner.addEventListener('click', function () {
console.log('inner');
}, true);
// addEventListener 最后一个参数使用 true,表示事件捕获
// 点击 inner,打印出
// outer
// in
捕获和冒泡的执行顺序
outer.addEventListener('click', function () {
console.log('bubble outer');
}, false); // 冒泡
inner.addEventListener('click', function () {
console.log('bubble inner');
}, false); // 冒泡
outer.addEventListener('click', function () {
console.log('outer');
}, true); // 捕获
inner.addEventListener('click', function () {
console.log('inner');
}, true); // 捕获
// 点击 inner,打印出
// outer
// bubble inner
// inner
// bubble outer
点击一个元素,元素即事件源,若事件源绑定了事件处理函数,且设定了事件捕获,则先执行捕获,捕获执行完毕后,按照绑定顺序执行该事件源绑定的事件,如果设定了事件冒泡,再执行冒泡
focus blur change submit reset select 事件没有冒泡和捕获,IE 浏览器没有事件捕获
阻止事件冒泡
阻止冒泡的方法
Event 的原型上有 stopPropagation 方法,可以阻止冒泡,是 w3c 的规范
Event 的原型上有 cancleBubble 属性,赋值为 true,可以阻止冒泡
addEventListener 绑定事件处理函数,拿到事件对象
var outer = document.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];
inner.addEventListener('click', function (ev) {
console.log(ev); // 事件对象 ev
ev.stopPropagation(); // 阻止事件冒泡
}, false);
IE 浏览器没有 stopPropagation 方法,可以使用 cancelBubble 属性
注意:IE 浏览器中事件对象存放在 window.event 中。IE8 不支持 addEventListener 方法
// 封装阻止冒泡的方法
function cancelBubble(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else ev.cancelBubble = true; // 兼容 IE8 及以下
}
// 使用上文中封装好的 addEvent 方法
function addEvent(elem, type, fn) {
if (elem.addEventListener) {
elem.addEventListener(type, fn);
} else if (elem.attachEvent) {
elem.attachEvent('on' + type, function (ev) {
fn.call(elem, ev);
});
} else {
elem['on' + type] = fn;
}
}
// 绑定事件处理函数
var outer = document.getElementsByClassName('outer')[0],
inner = outer.getElementsByClassName('inner')[0];
addEvent(inner, 'click', function (ev) {
var ev = ev || window.event; // IE 兼容性写法
cancelBubble(ev); // 阻止冒泡
});
阻止默认事件
三种方法
- 事件对象 preventDefault() 方法,兼容 IE9 及以上
- 事件对象 returnValue = false,兼容 IE8 及以下
- 事件处理函数 return false
兼容性写法
function preventDefaultEvent(ev) {
if (ev.preventDefault) {
ev.preventDefault();
} else ev.returnValue = false; // 兼容 IE8 及以下
}
右键菜单事件
document.oncontextmenu = function (ev) {
var ev = ev || window.event;
// 1. ev.preventDefault(); // IE9 及以上
// 2. ev.returnValue = false; // IE8 及以下
// 3. return false;
}
a 标签跳转事件
href 使用伪协议
<a href="javascript:void(0);">a 标签</a>
<a href="javascript:;">a 标签</a>
<a href="#">a 标签</a> <!--跳转到当前页面顶部-->
onclick 事件 return false
<a href="http://www.baidu.com" onclick="return false">a 标签</a>
<a href="http://www.baidu.com" onclick="return test(),false">a 标签</a>
<!--第二个是利用了 “,” 分隔符会返回最后一个的特点,与 test 方法无关-->
绑定事件处理函数
<!--内联绑定-->
<a id='taga' href="http://www.baidu.com" onclick="return test()">a 标签</a>
<!--句柄绑定-->
<script>
document.getElementById('taga').onclick = test;
function test(ev) {
var ev = ev || window.event;
// 1. ev.preventDefault(); // IE9 及以上
// 2. ev.returnValue = false; // IE8 及以下
// 3. return false;
}
// 前两种方式在使用内联属性绑定时,不需要在属性上加 return,第三种则需要
</script>
表单的 action 属性支持
javascript:伪协议,onsubmit 或者提交按钮点击事件都可以绑定处理函数,阻止提交的方法和阻止 a 标签跳转的方法类似
冒泡捕获流
事件流:描述从页面中接收事件的顺序
事件冒泡流:微软 IE 提出,Event Bubbling
事件捕获流:网景 Netscape 提出,Event Capturing
事件流三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段
元素触发事件时,首先事件捕获阶段,由父到子的执行事件处理函数,然后处于目标阶段,该元素的事件处理函数按绑定顺序执行,最后事件冒泡阶段,由子到父的执行事件处理函数
事件和事件源
事件即事件对象,可以由事件处理函数的参数拿到
IE8 及以下中事件对象存放在 window.event 中
// btn 按钮元素
btn.onclick = function(ev) {
var ev = ev || window.event; // IE8 兼容性写法
}
事件源即事件源对象,是发生事件的元素,即事件发送器,可以从事件对象中获取
IE8 及以下只有 srcElement,firefox 低版本只有 target,chrome 两者都有
// btn 按钮元素
btn.onclick = function(ev) {
var ev = ev || window.event; // IE8 兼容性写法
var tar = ev.target || ev.srcElement; // 获取事件源的兼容性写法
}
事件委托
事件委托也叫事件代理,指对父级元素绑定事件处理函数,通过获取事件源来处理子元素
示例:点击按钮使列表 ul 增加 li 元素,点击每个 li 元素打印出其中的内容(innerHTML)
如果不使用事件委托,需要循环对每个 li 进行绑定,点击按钮添加新的 li 元素后也要进行绑定,效率低下
使用事件委托,直接对 ul 绑定点击事件处理函数,获取事件对象、事件源对象,再对源对象进行处理
<body>
<button>btn</button>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var oBtn = document.getElementsByTagName('button')[0],
oList = document.getElementsByTagName('ul')[0],
oLi = oList.getElementsByTagName('li');
oBtn.onclick = function () {
var li = document.createElement('li');
li.innerText = oLi.length + 1;
oList.appendChild(li);
}
oList.onclick = function (ev) {
var ev = ev || window.event,
tar = ev.target || ev.srcElement;
// tar 即为被点击的 li 元素
console.log(tar.innerHTML);
// 返回在所有兄弟元素中的索引,借用数组 indexOf 方法
console.log(Array.prototype.indexOf.call(oLi, tar));
}
</script>
</body>
JavaScript DOM 事件模型的更多相关文章
- JavaScript DOM事件模型
早期由于浏览器厂商对于浏览器市场的争夺,各家浏览器厂商对同一功能的JavaScript的实现都不进相同,本节内容介绍JavaScript的DOM事件模型及事件处理程序的分类. 1.DOM事件模型.DO ...
- 走进javascript——DOM事件
DOM事件模型 在0级DOM事件模型中,它只是简单的执行你为它绑定的事件,比如你为某个元素添加了一个onclick事件,当事件触发时,它只是去调用我们绑定的那个方法,不再做其他的操作. 在2级DOM事 ...
- javascript中0级DOM和2级DOM事件模型浅析
Javascript程序使用的是事件驱动的设计模式,为一个元素添加事件监听函数,当这个元素的相应事件被触发那么其添加的事件监听函数就被调用: <input type="button&q ...
- javascript中0级DOM和2级DOM事件模型浅析 分类: C1_HTML/JS/JQUERY 2014-08-06 15:22 253人阅读 评论(0) 收藏
Javascript程序使用的是事件驱动的设计模式,为一个元素添加事件监听函数,当这个元素的相应事件被触发那么其添加的事件监听函数就被调用: <input type="button&q ...
- DOM事件模型浅析
1.何为DOM DOM是"Document Object Model"的缩写,中文译为"文档对象模型".它是一种跨平台.跨语言的编程接口,将HTML,XHTML ...
- W3C DOM 事件模型(简述)
1.事件模型 由于事件捕获与冒泡模型都有其长处和解释,DOM标准支持捕获型与冒泡型,能够说是它们两者的结合体.它能够在一个DOM元素上绑定多个事件处理器,而且在处理函数内部,thiskeyword仍然 ...
- JavaScript Dom 事件
JavaScript Dom 事件 对于事件需要注意的要点: // this标签当前正在操作的标签. this // event封装了当前事件的内容. even 常用事件 // 鼠标单击.触发事件 ...
- DOM事件: DOM事件级别、DOM事件流、DOM事件模型、DOM事件捕获过程、自定义事件
前端面试中只要问到事件,就肯定会有DOM事件:如果回答出来了,就会一直向下延申,其实这些东西都很简单,但我第一次被问到的时候,也是懵的: DOM事件级别: DOM0 element.onclick = ...
- javascript之事件模型
事件模型 冒泡型事件(Bubbling):事件由叶子节点沿祖先节点一直向上传递到根节点 捕获型事件(Capturing):由DOM树最顶元素一直到最精确的元素,与冒泡型事件相反 DOM标准事件模型:D ...
随机推荐
- org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: java.sql.SQLException: Incorrect string value: '\xE2\x80\x8B\xE2\x80\x8B...' for column 'pro_backgroud' at row
如果你在mysql数据库中,将所有的表的编码格式设置成为utf-8之后还是不行,那就试试这个吧:ALTER TABLE your_database_name.your_table CONVERT TO ...
- 曹工说Spring Boot源码(22)-- 你说我Spring Aop依赖AspectJ,我依赖它什么了
写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...
- nodejs通过响应回写的方式渲染页面资源
我们一般通过node框架提供的api操作页面渲染,如何利用原始回写的方式来实现同样的功能呢下面是通过node 提供的异步地读取一个文件的全部内容api readFile进行操作,代码如下: html ...
- flask连接mysql出现ModuleNotFoundError: No module named 'MySQLdb'
只需在配置SQLALCHEMY_DATABASE_URI时,加上pymysql就可以了: app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql ...
- python学习记录_IPython基础,Tab自动完成,内省,%run命令_
这是我第一次写博客,之前也有很多想法,想把自己所接触的,以文本的形式储存,总是没有及时行动.此次下定决心,想把自己所学,所遇到的问题做个记录共享给诸位,与此同时自己作为备忘,感谢各位访问我的博 ...
- 一起了解 .Net Foundation 项目 No.14
.Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. .NET Core .NE ...
- 复习笔记——1. C语言基础知识回顾
1. 数据类型 1.1 基本数据类型 整型:int, long,unsigned int,unsigned long,long long-- 字符型:char 浮点型:float, double-- ...
- PHP中elseif与else if的区别
在PHP中,正常情况下elseif和else if的用法及效果是一样的,但在使用带冒号的if语句时(也就是php替代)情况会有点不一样,比如: <?php /*正确的写法*/ $a = 1; ...
- 简说Python之IO
闺女,你在玩电脑什么游戏? 爸爸,我在玩植物大战僵尸呢. 闺女,你知道什么是输入输出设备吗? 爸爸,??? 你看,咱们的键盘和鼠标 ,就是可以控制那些植物的工具.这些发出指令的就是输入设备.咱们可以用 ...
- elementui 在原生方法参数里,添加参数
公司有个项目需求需要在一个列表中分别上传图片,饿了么的方法不支持传递index,可以这样传递: :on-change="(file,fileList)=>{return changeF ...