理解浏览器事件模型

understandEventModel.html 代码:

<!DOCTYPE HTML>
<html>
<head>
<title>Understand Event Model</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="../css/main.css"/>
<style>
img
{
display: block;
margin: auto;
}
</style>
</head>
<body>
<img id="example" src="data:images/example.jpg" alt="A bolt of lightning"
onclick="console.log('At ' + formatDate(new Date()) + ' BOOM!');" /> <p>The output is printed on the console</p> <!-- 在 onsubmit 中调用返回 false 的方法不能阻止默认动作 -->
<!-- <form id="myForm" action="www.cnblogs.com" onsubmit="doSubmit()"> -->
<form id="myForm" action="www.cnblogs.com" onsubmit="return false">
<div>
<label for="name">Name:</label>
<input id="name" name="name" type="text" />
</div>
<div>
<button type="submit">Click me!</button>
</div>
</form> <form id="myForm2" action="www.cnblogs.com" onsubmit="prevent()">
<div>
<label for="name2">Name:</label>
<input id="name2" name="name2" type="text" />
</div>
<div>
<button type="submit">Click me2!</button>
</div>
</form> <div id="dom2">
<button id="multipleButton" type="button">multiple events</button>
</div> <div id="firstLevel">
<div id='secondLevel'>
<input id="testButton" type="button" value="捕获和冒泡" />
</div>
</div> <script src="js/understandEventModel.js"></script>
</body>
</html>

understandEventModel.js:

/*
DOM Level 用来表示实现 W3C DOM 规范的级别要求
DOM Level 0 Event Model,在这个模型背后,事件处理器通过把函数赋值给元素的属性来声明实现
*/
function formatDate(date) {
return (date.getHours() < 10 ? '0' : '') + date.getHours() +
':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() +
':' + (date.getSeconds() < 10 ? '0' : '') + date.getSeconds() +
'.' + (date.getMilliseconds() < 10 ? '00' : (date.getMilliseconds() < 100 ? '0' : '')) +
date.getMilliseconds();
} /*
id 为 example 的图片在 HTML 代码中直接在标签属性 onclick 中声明了一个 click 事件,这个属性值会被使用创建一个
匿名函数体,类似下面的代码,直接在标签属性中声明事件不是推荐的做法
*/
document.getElementById('example').onmouseover = function(event) {
console.log(this);
console.log('At ' + formatDate(new Date()) + ' Crackle!'); //当触发事件处理器时,在所有兼容标准的浏览器里,一个名为 Event 的对象会作为第一参数传递给处理器,只有 IE9 以上,
//Firefox,Chrome, Safari 和 Opera 支持, IE8 之前的浏览器可以通过自己的方式把 Event 对象传给全局的 event 属性
event = event || window.event; //获取目标元素的引用,也就是触发事件的元素,必须通过兼容标准浏览器的 target 属性获取,但是旧的 IE 使用
//的是 srcElement
var target = event.target || event.srcElement;
}; /*
事件冒泡
目标元素有机会处理事件之后,浏览器的事件处理机制会检查元素的父元素是否包含此事件类型的处理器,如果有也会调用,再
依次检查爷爷元素,直到 DOM 树的顶部
*/
var elements = document.getElementsByTagName('*'); //得到页面上的所有元素 for(var i = 0; i < elements.length; i++){
if(elements[i].id != 'example'){
(function(current){
current.onclick = function(event){
event = event || window.event;
var target = event.target || event.srcElement; console.log('At ' + formatDate(new Date()) +
' For ' + current.tagName + '#' + current.id +
' target is ' + target.tagName + '#' + target.id);
}
})(elements[i]);
}
} /*
要阻止事件的传播,在现代浏览器中可以调用 Event 实例的 stopPropagation() 方法
在旧的 IE 浏览器中可以设置 Event 实例的 cancelBubble 属性为 true 实现,现在很多新的浏览器也提供了该属性,尽管
这不是 W3C 标准(我觉得这个实践中还是不用的好,知道作用就好)
有些事件是默认动作,比如 form 的 submit,要取消这些语义动作,在新的浏览器调用 Event 实例的 preventDefault() 方法。旧的
IE 中没有此方法,设置 returnValue 属性的值为 false;另一种方法是处理器代码返回 false
<form name="myForm" onsubmit="return false;" ... >
*/ /*
被页面的 onsubmit 属性引用,但没有成功阻止默认的 submit 动作
*/
function doSubmit(){
return false;
} /*
阻止了默认的 submit 动作
*/
function prevent(){
event = event || window.event;
event.preventDefault();
} /*
DOM Level 2 event model
DOM Level 0 event model 的缺点是每个元素每次只能注册一种特定类型的事件处理器,主要是因为属性用来存储
事件处理器函数的引用
DOM Level 2 event model 就是为了解决上面的问题而建立的。这里的事件处理器通过标准的元素方法而不是元素属性赋值
来实现,每个 DOM 元素都实现了一个 addEventListener() 方法。
addEventListener(eventType, listener, useCapture)
eventType: 要处理的事件类型,DOM 0 中使用的事件,比如 click
listener:表示元素事件处理器的函数
useCapture:布尔值
*/
var multipleButton = document.getElementById('multipleButton'); /*
这个 multipleButton 按钮在下面代码增加事件处理器之前,上面的代码中增加了 click 事件是处理函数,从结果可以看出,
这个处理函数有被保留,而且是作为第一个触发
event.stopPropagation() 随便写在哪个处理函数中效果都一样,不会事件冒泡到父元素
*/
multipleButton.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) + ' BOOM once!');
//event.stopPropagation();
}); multipleButton.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) + ' BOOM twice!');
//event.stopPropagation();
}); multipleButton.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) + ' BOOM thrice!');
event.stopPropagation();
}); /*
DOM Level 2 event model 中的事件传播
事件首先从 DOM 树根结点向目标元素传播,然后从目标元素向 DOM 根节点冒泡传播。前面的阶段成为捕获阶段,后者
是冒泡阶段 addEventListener(eventType, listener, useCapture) 方法中的 useCapture 是用来标记函数作为何种处理器使用的,false 为
冒泡阶段处理器,true 为捕获阶段处理器,默认为 false
*/ /*function eventLog(curElement, event){
event = event || window.event;
console.log('At ' + formatDate(new Date()) +
' Capture for ' + curElement.tagName + '#' + curElement.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}*/ var firstLevel = document.getElementById('firstLevel');
var secondLevel = document.getElementById('secondLevel');
var testButton = document.getElementById('testButton'); firstLevel.onclick = function(){};
secondLevel.onclick = function(){};
testButton.onclick = function(){}; //捕获阶段运行
firstLevel.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Capture for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, true); firstLevel.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Bubble for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, false); secondLevel.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Capture for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, true); secondLevel.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Bubble for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, false); testButton.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Capture for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, true); testButton.addEventListener('click', function(event){
console.log('At ' + formatDate(new Date()) +
' Bubble for ' + this.tagName + '#' + this.id ) +
' target is ' + event.target.tagName + '#' + event.target.id;
}, false);

jQuery 事件模型

jQueryEventModel.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Events in jQuery Example - jQuery in Action, 3rd edition</title>
<link rel="stylesheet" href="../css/main.css"/>
<style>
img
{
display: block;
margin: auto;
}
#wrapper
{
border: 1px solid #3A5895;
padding: 10px;
} #address:focus
{
border: 3px solid #000000;
}
.outer
{
position: relative;
width: 200px;
height: 100px;
border: 1px solid #000000;
background-color: #55AA55;
} .inner
{
width: 50%;
height: 50%;
margin: 0.5em auto;
border: 1px solid #000000;
background-color: #FFFF4F;
} #outer2
{
margin-top: 2em;
}
</style>
</head>
<body>
<img id="example" src="data:images/example.jpg" alt="A bolt of lightning"/> <p>
<button id="btn1">Click me!</button>
<button id="btn2">Don't click me!</button>
</p> <ul id="myList"></ul> <p>
<button id="btn">Does nothing</button>
<button id="btn-attach">Attach handler</button>
<button id="btn-remove">Remove handler</button>
</p> <p>
<div id="foo">
Trigger part
</div>
</p> <div id="wrapper">
<button id="btnTrigger">Click me!</button>
<button id="anotherBtn">Trigger custom event</button>
<input type="text" id="address" />
</div> <div class="outer" id="outer1">
Outer 1
<div class="inner" id="inner1">Inner 1</div>
</div>
<div class="outer" id="outer2">
Outer 2
<div class="inner" id="inner2">Inner 2</div>
</div> <p>
<button id="btnTestNamespacing">Test Namespacing</button>
<button id="btn-remove-by-namespacing">Remove by namespacing</button>
</p> <p>The output is printed on the console</p> <script src="../js/jquery-3.2.1.js"></script>
<script src="js/jQueryEventModel.js"></script>
</body>
</html>

jQueryEventModel.js:

/*
jQuery Event Model
1. 为创建事件处理器提供统一方法
2. 允许每个元素、每个事件类型注册多个方法
3. 适用标准事件名称,如 click 或 mouseover
4. 传递 Event 实例作为第一个参数
5. 规范事件实例中最常用的属性
6. 为事件取消和阻止操作提供统一的方法 除了不支持捕获阶段,jQuery 事件模型几乎完美支持 DOM Level 2 Event Model
*/ function formatDate(date) {
return (date.getHours() < 10 ? '0' : '') + date.getHours() +
':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() +
':' + (date.getSeconds() < 10 ? '0' : '') + date.getSeconds() +
'.' + (date.getMilliseconds() < 10 ? '00' : (date.getMilliseconds() < 100 ? '0' : '')) +
date.getMilliseconds();
} /*
on(eventType[, selector][, data], handler)
on(eventHash[, selector][, data])
为选择的元素的一个或多个事件添加一个或多个事件处理器 eventType(String):事件类型的名称,多个事件类型可以用空格分隔
selector(String):过滤器用来过滤选中元素的子元素,这些子元素触发事件
data(Any):传递给 Event 实例的数据,赋值给 data 属性
handler(Function):事件处理器的函数,冒泡阶段的当前元素作为函数上下文,false 值表示函数 return false eventHash(Object):单个调用中为多个事件类型建立处理器的对象。属性名区分事件类型,属性值提供事件处理器 返回 jQuery 集合
*/
$('#example')
.on('click', function (event) {
console.log('At ' + formatDate(new Date()) + ' BOOM once!');
})
.on('click', function (event) {
console.log('At ' + formatDate(new Date()) + ' BOOM twice!');
})
.on('click', function (event) {
console.log('At ' + formatDate(new Date()) + ' BOOM thrice!');
})
.on('mouseenter mouseleave', function(event){
console.log(event.type)
}); $('#btn1').on('click', function(){
console.log('The button is clicked!');
}).on('mouseenter mouseleave', myFunctionHandler); /*
使用 eventHash 参数类型的代码
*/
$('#btn2').on({
click: function(){
console.log('Oh no, you clicked me!');
},
mouseenter: myFunctionHandler,
mouseleave: myFunctionHandler
}); function myFunctionHandler(event){
event.stopPropagation();
console.log(event.type + ' ' + event.target.id);
} /*
注意这里的第二个参数,设置了 data 参数后,可以通过 event 参数的 data 属性访问
*/
$('#btn1').on('click', {
name: 'Martin_Fu'
}, function(event){
console.log(event.data.name + ' clicked the button!');
}); /*
事件委托(event delegation)
这是一种向元素的父元素添加事件处理器的重要技术,可以为不存在的元素添加事件处理器
*/
$('<li>item1</li>').add($('<li>item2</li>')).appendTo('#myList');
/*
原生 JS 写法
*/
document.getElementById('myList').addEventListener('click', function(event){
if(event.target.nodeName === 'LI'){
console.log('List item: ' +
(Array.prototype.indexOf.call(document.getElementById('myList').children, event.target) + 1));
}
}); /*
这里注意第二个参数,它会对子元素进行筛选
事件委托的优势不只局限于为不存在的元素执行事件处理器,更可以节省内存和时间。比如 myList 下有很多 <li> ,那么需要循环
添加事件处理器,如果 <li> 元素很多,那么会耗费不少时间,由于 JS 是单线程的,会导致不好的用户体验。
但也不能因为这个原因给一个元素(比如 document )添加过多处理器,同样影响性能,建议尽可能为离目标元素近的元素添加处理器
*/
$('#myList').on('click', 'li', function(event){
console.log('List item(jQuery): ' + ($(this).index() + 1));
}); /*
one(eventType[, selector][, data], handler)
one(eventHash[, selector][, data])
为选择的元素的一个或多个事件添加一个或多个事件处理器,一旦执行,事件处理器会自动删除 返回 jQuery 集合
*/ /*
off(eventType[, selector][, data], handler)
off(eventHash[, selector][, data])
删除参数指定的 jQuery 对象中所有元素的事件处理器,如果没有提供参数,那么删除所有的元素处理器 返回 jQuery 集合
*/
/*
这里注意一点,如果多次点击 btn-attach 按钮,btn 按钮会添加两个 click 事件,但是只要点击一次
btn-remove 按钮,这两个事件都会移除
*/
var $btn = $('#btn');
var counter = 1; function logHandler(event){
console.log('Click ' + counter);
counter++;
} $('#btn-attach').on('click', function(event){
$btn.on('click', logHandler).text('Log');
}); $('#btn-remove').on('click', function(event){
$btn.off('click', logHandler).text('Does nothing');
}); /*
触发事件
有时候,系统使用脚本控制事件的触发和执行过程
调用作为处理器的函数不会导致语义动作或者冒泡传播发生
*/
/*
trigger(eventType[, data])
为所有匹配的元素调用传递事件类型建立任意事件处理器和行为,全力模拟事件被触发,包括 DOM 层级传播和语义动作执行 eventType(String|jQuery.Event):指定要调用的事件类型的名字,也可以包含命名空间;还可以传递 jQuery.Event
data(Any):传递给处理器的数据,如果是数组,传递的元素会作为不同的参数 返回 jQuery 集合
*/
$('#foo').on('click', function(event, par1, par2, par3){
console.log(event.type + "_" + event.target.id);
console.log(par1, par2, par3);
}); /*
这里要注意的是参数传递的是数组
*/
$('#foo').trigger('click', [1, 2, 3]); /*
triggerHandler(eventType[, data])
为匹配的元素(只是集合中的第一个元素)调用建立的事件处理器,没有出现冒泡传播、语义动作或者实时事件 eventType(String|jQuery.Event):指定要调用的事件类型的名字,也可以包含命名空间;还可以传递 jQuery.Event
data(Any):传递给处理器的数据,如果是数组,传递的元素会作为不同的参数 返回最后一个处理器的返回值,如果没有返回值,返回 undefined
*/
$('#wrapper')
.on('focus', function(event){
console.log('Div focused!');
})
.on('click', function(event){
console.log('Div clicked!');
}); $('#address')
.on('focus', function(event){
console.log('Input focused!');
})
.on('click', function(event){
console.log('Input clicked!');
}).triggerHandler('focus'); $('#btnTrigger')
.on('click', function(event){
console.log('Button clicked!');
})
.trigger('click'); /*
快捷方式
编写 on() 和 off() 完整语法很多时候会变得烦人,因此 jQuery 提供了快捷方式
eventName([data, ] handler)
通过方法名为指定事件使用的函数建立事件处理器,支持的方法如下:
blur focusout mouseleave resize
change keydown mousemove scroll
click keypress mouseout select
dbclick keyup mouseover submit
focus mousedown mouseup
focusin mouseenter ready data(Any):作为 Event 实例的 data 属性传递给处理器的数据
handler(Function):作为事件处理器的函数 返回 jQuery 集合
*/
$('#btnTrigger')
.dblclick(function(){
console.log('Button double clicked!');
})
.dblclick(); /*
hover(enterHandler, leaveHandler)
hover(handler)
为匹配元素的 mouseenter 和 mouseleave 事件建立处理器 enterHandler(Function):作为 mouseenter 处理器的函数
leaveHandler(Function):作为 mouseleave 处理器的函数
handler(Function):对于 mouseenter 和 mouseleave 事件都会调用的单个处理器
*/
function report(event) {
event.stopPropagation();
console.log(event.type + ' on ' + event.target.id);
} $('#outer1').on('mouseenter mouseleave', report);
$('#inner1').on('mouseenter mouseleave', report);
$('#outer2').hover(report);
$('#inner2').hover(report); /*
自定义事件
*/
$('#btnTrigger').on('customEvent', function(){
console.log('Custom event invoked!')
}); $('#anotherBtn').click(function(){
$('#btnTrigger').trigger('customEvent');
}); /*
为事件添加命名空间
事件是后缀命名空间,用圆点分割
*/
$('#btnTestNamespacing')
.on('click.editMode.myApp', function(){
console.log('Click event under editMode namespacing invoked!');
})
.on('mouseenter.editMode', function(){
console.log('Mouseenter event under editMode namespacing invoked!');
})
.on('mouseleave.myApp', function(){
console.log('Mouseleave event under editMode namespacing invoked!');
}); $('#btn-remove-by-namespacing').click(function(){
$('#btnTestNamespacing').off('.editMode');
})

jQuery 实战读书笔记之第六章:事件本质的更多相关文章

  1. jQuery 实战读书笔记之第五章:使用 jQuery 操作页面

    html 如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> &l ...

  2. jQuery 实战读书笔记之第四章:使用特性、属性和数据

    使用属性 /* 每个元素都有一或多个特性,,这些特性的用途是给出相应元素或其内容的附加信息.(出自 JavaScript 高级程序设计) */ /* 特性是固有的 JavaScript 对象 属性指的 ...

  3. jQuery 实战读书笔记之第三章:操作 jQuery 集合

    创建新 HTML 元素 $('<div>Hello</div>'); /* 创建等价的空 div 元素 */ $('<div>'); $('<div /> ...

  4. Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧

    第六章 Android绘图机制与处理技巧 1.屏幕尺寸信息屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DP ...

  5. 《深入理解java虚拟机》读书笔记五——第六章

    第六章 类文件结构 1.无关性的基石 各种不同平台的虚拟机与所有平台都统一使用程序存储格式——字节码是构成平台无关的基石. 实现语言无关性的基础仍然是虚拟机和字节码存储格式,Java虚拟机不和包括Ja ...

  6. jQuery 实战读书笔记之第二章:选择元素

    基本选择器 html 代码如下,后面的 js 使用的 html 基本大同小异. <!doctype html> <html> <head> <title> ...

  7. Javascript高级程序设计读书笔记(第六章)

    第6章  面向对象的程序设计 6.2 创建对象 创建某个类的实例,必须使用new操作符调用构造函数会经历以下四个步骤: 创建一个新对象: 将构造函数的作用域赋给新对象: 执行构造函数中的代码: 返回新 ...

  8. jQuery实战读书笔记(备忘录)

    选择器备忘: | :even 匹配所有索引值为偶数的元素,从 0 开始计数 :odd 匹配所有索引值为奇数的元素,从 0 开始计数 实例——设置table交替行变色: <script type= ...

  9. 初读"Thinking in Java"读书笔记之第六章 --- 访问权限控制

    包:库单元 包内包含有一组类,他们在单一的名字空间下被组织在一起. 通过import ***.***.*可以将某个包下的所有类导入到当前文件中. 每个Java源文件最多只能有一个public类,且名称 ...

随机推荐

  1. deferred rendering with msaa

    https://docs.nvidia.com/gameworks/content/gameworkslibrary/graphicssamples/d3d_samples/antialiasedde ...

  2. Django 工作流程

    一.Django 工作流程 在开始具体的代码之旅前,先来宏观地看下Django是如何处理Http Resquest的,如下图: 假设你已经在浏览器输入了 http://127.0.0.1:8000/p ...

  3. 摄像头模组 PDAF对焦(Phase Detection Auto Focus)

    本文主要是最近看的两个文档的总结,相对零散的笔记,包括<imx298 software reference PDAF>与<PDAF Truly>. 1.PDAF功能的实现需要使 ...

  4. jQuery.toggleClass() 和detach()方法详解

    一.toggleClass()函数: toggleClass()函数用于切换当前jQuery对象所匹配的每一个元素上指定的css类名.所谓"切换",就是如果该元素上已存在指定的类名 ...

  5. jTemplates模板学习笔记

    1.jTemplates工作方式   1)setTemplateElement:指定可处理的模板对象 2)processTemplate:对模板化的对象进行数据处理 2.语法解析   1)jTempl ...

  6. LightOJ - 1265 Island of Survival 期望

    题目大意:有一个生存游戏,里面t仅仅老虎,d仅仅鹿,另一个人,每天都要有两个生物碰面,如今有下面规则 1.老虎和老虎碰面.两仅仅老虎就会同归于尽 2.老虎和人碰面或者和鹿碰面,老虎都会吃掉对方 3.人 ...

  7. 【云计算】OpenStack项目全面介绍

    关于OpenStack孵化项目trove(DBaaS)之我见:http://blog.csdn.net/ddl007/article/details/17253751 OpenStack Trove将 ...

  8. [Algorithm] Find Max Items and Max Height of a Completely Balanced Binary Tree

    A balanced binary tree is something that is used very commonly in analysis of computer science algor ...

  9. 亲測,Eclipse报&quot;An error has occurred,See error log for more details. java.lang.NullPointerException&quot;

    Eclipse报"An error has occurred,See error log for more details. java.lang.NullPointerException&q ...

  10. 创建CSS3警示框渐变动画

    来源:GBin1.com 在线演示   在线下载 现代的网页设计技术已经允许开发人员在大多数浏览器中快速实现所支持的动画,其中消息警示是非常普遍的.由于默认的JavaScript警示框往往设计不佳和过 ...