1. 何为 DOM
DOM 是 "Document Object Model" 的缩写, 中文译为 "文档对象模型". 它是一种跨平台, 跨语言的编程接口, 将 html,XHTML,xml 文档映射成树形结构, 树的每一个节点都是一个对象. 正因如此, 面向对象的编程语言 (如 JavaScript) 可以通过 DOM 对 HTML,XHTML,xml 文档进行操作. 对于 HTML 文档来说, 它的根结点为 document 对象, HTML 元素为 element 对象, HTML 元素的属性为 attr 对象.
2. 何为 DOM 事件及如何对其作出响应
在浏览网页时, 我们常常需要页面对用户的操作作出响应, 比如点击 "阅读全文" 后我们期望页面展示被折叠的文本, 按下回车键后浏览器提交已填好的表单. 用户的各种操作都是 "事件". 事件都是在对象上发生的, 可能是 DOM 对象, BOM 对象, 等等. 事件发生后, 对象可能会作出响应, 也有可能 "无动于衷". 我们希望 DOM 元素对事件作出响应, 一般而言有两种方法:
i. 事件属性
事件属性是一种特殊的属性, 它的值规定了对应事件发生时需要执行的 JavaScript 脚本. 例:
<button onclick="console.log('button clicked!')"></button>
上面为 button 标签添加了事件属性 onclick, 其值为 "console.log('button clicked!')", 它规定了当元素被鼠标点击时, 控制台输出'button clicked'.
ii.addEventListener()方法
EventTarget.addEventListener()方法将指定的监听器注册到 EventTarget 上, 当该对象触发指定的事件时, 指定的回调函数就会被执行. EventTarget 可以是 element 对象, document 对象或者任何其他支持事件的对象. 例:
- <!--html 文件中 -->
- <button id='mybutton'>
- </button>
- // 脚本中 var mybutton=document.getElementById('mybutton'); mybutton.addEventListener('click',function(e){console.log('button
- clicked!');});
上例为 button 元素注册了 click 事件的监听器, 并规定事件时触发控制台输出'button clicked'.
3.DOM 事件模型
在讲解 DOM 事件模型前, 再用一个例子作为引入. 请看下面的 HTML 文件:
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset="utf-8">
- <title>
- DOM Event Model
- </title>
- <style>
- div{position: absolute;} #outer{ top: 100px; left: 100px; width: 600px;
- height: 400px; background-color: #aff; } #inner1,#inner2{ top: 50px; width:
- 200px; height: 300px; background-color: #f9a; } #inner1{left: 50px;} #inner2{left:
- 350px;} #core{ left: 50px; top: 50px; width: 100px; height: 150px; background-color:
- #f50; }
- </style>
- </head>
- <body>
- <div id='outer' onclick="console.log(this.id)">
- <div id='inner1' onclick="console.log(this.id)">
- </div>
- <div id='inner2' onclick="console.log(this.id)">
- <div id='core' onclick="console.log(this.id)">
- </div>
- </div>
- </div>
- </body>
- </HTML>
这里为 id 分别为 outer,inner1,inner2,core 的 4 个元素定义了事件属性, 元素被点击后将在控制台输出它的 id. 现在问题来了:
如果我点击 core 元素, 控制台将会输出什么?
点击 core 元素时, 由于 core 元素包含在 inner2 元素里, inner2 元素同样被点击了; 同理, inner2 元素包含在 outer 元素里, 那么 outer 元素也被点击了. 这种情况下哪一个元素的 click 事件将会被触发, 或者说三者都被触发? 如果说三者都被触发, 那么又是以怎样的顺序被触发?
我在火狐浏览器做了一次实验, 控制台输出结果如下:
core inner2 outer
也就是说, 三者的事件都被触发了, 且是 "由内向外" 触发的.
下面我们再做一个有趣的实验: 我们将上面的 HTML 文件再做一个小小的改动, 将 core 元素的样式
left: 50px;
改为
left: -250px;
此时观察页面我们会发现, 尽量 core 是 inner2 的子节点, 但由于我们定义了 "怪异" 的样式, 它跑到了 inner1 里面. 现在我们再次用鼠标点击 core, 观察控制台的输出:
core inner2 outer
和刚才的结果一模一样! 尽管表面上 inner1 似乎被点击了, 但它的 click 事件并没有触发; 反而是看似未被点击的 inner2 元素的 click 事件被触发了. 仿佛 core 元素的 click 事件被触发后, click 事件一层一层向上 "传播" 给了父节点.
为了解释刚才的实验结果, 是时候开始讲解 DOM 事件模型了.
当一个事件发生时, 事件会在 DOM 树中进行传播. 传播分为两个阶段:
i. 捕获阶段
在此阶段, 事件从根结点 (即 document 结点) 开始向下传播, 直到事件源所在元素.
ii. 冒泡阶段
在此阶段, 事件从事件源开始向上传播, 直到根结点.
拿刚才的例子来说, 事件传播的顺序为:
document 捕获 ->HTML 捕获 ->body 捕获 ->outer 捕获 ->inner2 捕获 ->core 捕获 ->core 冒泡 ->inner2 冒泡 ->outer 冒泡 ->HTML 冒泡 ->document 冒泡
对于事件属性, 默认在冒泡阶段触发事件. 如果用 addEventListener()方法注册监听器, 则可以指定在捕获阶段还是冒泡阶段触发事件: 如果最后一个参数为 false(默认值), 则在冒泡阶段触发事件; 如果为 true, 则在捕获阶段触发事件.
一般来说, 我们推荐采用 addEventListener()方法来注册监听器, 而尽量不用事件属性. 因为事件属性不利于行为与结构的分离, 使代码难以维护.
来源: http://www.bubuko.com/infodetail-3280177.html