放自己用DOM写的作品链接
写于2017.08.07
一、关于DOM
- 什么是DOM
DOM是JavaScript操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个JavaScript对象,从而可以用脚本进行各种操作(比如增删内容)。
浏览器会根据DOM模型,将结构化文档(比如HTML和XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。所以,DOM可以理解成网页的编程接口。
严格地说,DOM不属于JavaScript,但是操作DOM是JavaScript最常见的任务,而JavaScript也是最常用于DOM操作的语言 - DOM的节点
DOM的最小组成单位叫做节点(node)。文档的树形结构(DOM树),就是由各种不同类型的节点组成。每个节点可以看作是文档树的一片叶子。
节点的类型有七种。
*Document:整个文档树的顶层节点
*DocumentType:doctype标签(比如<!DOCTYPE html>)
*Element:网页的各种HTML标签(比如<body>、<a>等)
*Attribute:网页元素的属性(比如class="right")
*Text:标签之间或标签包含的文本
*Comment:注释
*DocumentFragment:文档的片段
DOM树如下图:
具体DOM学习建议参考链接:http://javascript.ruanyifeng.com/#dom
二、事件模型
什么是事件
事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信。一般来讲,有一下特点:
1、某某订阅了/关注/监听了×××
2、×××发生变化
3、某某得到通知
体现在DOM机制里就是:用户的操作发生变化,代码得到通知事件EventTarget接口
DOM的事件操作(监听和触发),都定义在EventTarget接口。Element节点、document节点和window对象,都部署了这个接口。此外,XMLHttpRequest、AudioNode、AudioContext等浏览器内置对象,也部署了这个接口。
该接口就是3个方法:
1、addEventListener:绑定事件的监听函数
例:
target.addEventListener(type, listener[, useCapture]);
type:事件名称,大小写敏感。
listener:监听函数。事件发生时,会调用该监听函数。
useCapture:布尔值,默认为false(监听函数只在冒泡阶段被触发)。true是在捕获阶段触发
addEventListener方法可以为当前对象的同一个事件,添加多个监听函数。这些函数按照添加顺序触发,即先添加先触发。如果为同一个事件多次添加同一个监听函数,该函数只会执行一次,多余的添加将自动被去除(不必使用removeEventListener方法手动去除)。
2、removeEventListener:移除事件的监听函数
例:
div.addEventListener('click', listener, false);
removeEventListener方法移除的监听函数,必须与对应的addEventListener方法的参数完全一致,而且必须在同一个元素节点,否则无效。
3、dispatchEvent:触发事件
dispatchEvent方法在当前节点上触发指定事件,从而触发监听函数的执行。该方法返回一个布尔值,只要有一个监听函数调用了Event.preventDefault(),则返回值为false,否则为true
例:
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
上面代码在当前节点触发了click事件-
3种绑定监听函数的方法
1、HTML标签的on-属性
例:元素标签的属性中,直接定义某些事件的监听代码
<body onload="doSomething()">
<div onclick="console.log('触发事件')">
上面代码为body节点的load事件、div节点的click事件,指定了监听函数。
使用这个方法指定的监听函数,只会在冒泡阶段触发另外,Element元素节点的setAttribute方法,其实设置的也是这种效果。
el.setAttribute('onclick', 'doSomething()');
2、Element节点的事件属性
Element节点对象有事件属性,同样可以指定监听函数。
例:
window.onload = doSomething;
div.onclick = function(event){
console.log('触发事件');
};
使用这个方法指定的监听函数,只会在冒泡阶段触发。
3、addEventListener方法
window.addEventListener('load', doSomething, false);(详解如上)
总结:第一种“HTML标签的on-属性”,违反了HTML与JavaScript代码相分离的原则;第二种“Element节点的事件属性”的缺点是,同一个事件只能定义一个监听函数,也就是说,如果定义两次onclick属性,后一次定义会覆盖前一次。 事件的捕获与冒泡阶段
1、操作系统最先知道用户点击了鼠标,浏览器次之
2、child被点击了,意味着parent也被点击了
3、那么如果同时监听了child和parent,谁先通知我
例子:如果对parent和child同时设置了监听,那么当我点击child,谁先通知我
捕获阶段:parent先通知,child后通知(一般不用)
冒泡阶段:child先通知,parent后通知e.target与e.currentTarget
e.target代表你点击的那个元素,而e.currentTarget代表你监听的那个元素,如果你处于捕获或者冒泡阶段,二者是不一样的阻止默认事件
document.querySelector("a").addEventListener('click',function(e){
e.preventDefault()
})
这样的话a就不会跳转了。如果在父元素阻止默认事件,那a也不跳转了,所以一般不建议这样做停止冒泡
e.stopPropagation( )(在子元素加入,则不通知爸爸了,不会再向上传播)事件委托
由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件委托
例:
HTML代码:
<ul>
<li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
</ul>
JS代码:
var ul =document.querySelector('ul')
function fn(e){
if(e.target.tagName ==="LI"){
console.log("ok")(如果li里面裹一层span,点击span就不能执行监听函数)
}
}
ul.addEventListener('click',fn)
优化执行函数:
function fn(e){
var el = e.target
while(el.tagName !=="LI"){
el =el.parentNode(向上遍历父元素)
}
if(el){
console.log('yes')
}
}
但是实际上我只需要遍历到我监听的元素就可以了,如果直到遍历到监听的元素还没有的话,就认为没有了
例:
HTML代码:
<div>
<p>我是<span>p</span></p>
<hi>我是<span>h1</span></hi>
</div>
JS代码:
var div =document.querySelector('div')
function fn(e){
var el = e.target
while(el.tagName !=="H1"){
el =el.parentNode(向上遍历父元素)
if(el===div){
el=null
break;
}
}
if(el){
console.log('yes')
}
}
div.addEventListener('click',fn(e))