文章目录
  1. 1. HTML-DOM
  2. 2. 事件处理
    1. 2.1. 添加事件
    2. 2.2. 移除事件
  3. 3. 事件的触发过程
    1. 3.1. 事件捕捉
    2. 3.2. 事件冒泡
    3. 3.3. 事件委托
    4. 3.4. 事件拦截
    5. 3.5. 事件模拟
    6. 3.6. 自定义事件

#DOM-CORE & HTML-DOM

对于什么是DOM而言,之前,已经讨论过了,现在我们重新回过头来,分析和思考一下,有关于DOM的一些分类上的区别。

什么是 DOM?
DOM 是 W3C(万维网联盟)的标准。
DOM 定义了访问 HTML 和 XML 文档的标准:
“W3C 文档对象模型 (DOM) 是中立于平台和语言的接口,它允许程序和脚本动态地访问和更新文档的内容、结构和样式。”
W3C DOM 标准被分为 3 个不同的部分:
核心 DOM - 针对任何结构化文档的标准模型
XML DOM - 针对 XML 文档的标准模型
HTML DOM - 针对 HTML 文档的标准模型

上文是w3c对DOM的分类,基本上就是DOM-CORE 和HTML-DOM,XML-DOM不在讨论范围以内,我们暂且不管。
先说DOM-CORE吧

##DOM-CORE

满足软件开发者和Web脚本编写者,访问和操控产品项目中包含的可解析的HTML和XML内容。这个是COM-CORE的定义,从定义上看,HTML-CORE并不是单独提供给javascript的哦,也就是说,其他的语言也是可以通过HTML-CORE来操作DOM的哦,嗯,为了证实这一点,我们就用全世界最好的语言(php)来写一段。

1
<?php
$Doc = new DOMDocument();
$Doc->load("test.html");

$d = $Doc->documentElement;
foreach ($d->childNodes AS $item)
  {
  print $item->nodeName . " = " . $item->nodeValue . "<br />";
  }
?>

这里用到了一段html

1
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>test</title>
</head>
<body>
    <p>hello world</p>
</body>
</html>

嗯,然后,你就会得到以下的输出:

1
#text = 
head = test 
#text = 
body = hello world 
#text =

虽然不是很强大,但是也是可以的哦,嗯,以上代码的运行环境在php5以上。更多的内容参考这里

回到js的上面,DOM-CORE对应的操作其实更多的是体现在DOM-CORE是基于节点的(NODE-BASE),其所有的操作都是基于该节点的操作,如下:

1
<a href="url" id="id" >this is a a elemt </a>
<script>
//获取 #id 的href属性值
    var node = document.getElementById('id');
    var url = node.attattributes['href'].value;
//监听 #id 的click事件
    node.onclick = function(e){
        //do some thing
    }
//创建一个节点
    document.createElement();
    document.createTextNode();
</script>

简单的说,其实HTML-DOM和DOM-CORE并不是一个相互排斥的的关系,而是一个包含和被包含的关系,HTML-DOM是DOM-CORE的对于html操作的子集,但是HTML-DOM还有一点他自己的独特个性。

HTML-DOM

HTML之中特定元素的功能,和恰到好处的、易用的、针对常见性任务的HTML文档操作机制。HTML模块的意义也在于解决了向后兼容的问题。
也就是说,HTML-DOM是专门针对html对HTML-CORE做的优化操作,使之能够更好的为html服务,但是呢,HTML-DOM还有它自己的个性,这个个性在于它有一套自己的体系,它是基于元素(Element-based)来提供自身的接口的。
看下面的例子。

1
<a href="url" id="id" >this is a a elemt </a>
<script>
//获取 #id 的href属性值
    var node = document.getElementById('id');
    var url = node.getAttributes('href');
//监听 #id 的click事件
    node.addEventLisenter(function(e){
        //do some thing
    });
//创建一个节点
    document.createElement();
    document.createTextNode();
</script>

嗯,你会发现两者还是长的蛮像的哈,但是,HTML-DOM是有扩充哦,对于HTML-DOM的addEventListener方法来说,往这个方法添加的点击事件是可以维系成一个点击事件队列的,在队列里面,所有的匿名函数都可以得到执行,但是,对于DOM-CORE来说,他对于点击事件的监听就要low的多了,他并不可以形成一个事件队列而是只会执行最后一次定义的click事件函数,这一点和css的渲染机制是一样的。看下面的例子:
一直html文档中有如下标签

1
<a href="url" id="id">click me</a>

DOM—CORE的事件处理是这样的

1
var node = document.getElementById('id');
node.onclick = function(e){
    // do some thing
}

node.onclick = function(e){
    //do other thing
}

然而,最后执行的只有other thing。。。
但是

1
node.addEventListener("click",function(){
    //do some thing
})

node.addEventListener("click",function(){
    //do other thing
})

下的话,两个事件里的函数都是会被执行哦,很明显,HTML-DOM对于DOM-CORE有了自己的进一步优化,并不是单纯的只是包含和被包含哦。

##创建和添加节点
创建和添加节点其实都很简单,使用指定的函数就可以了。

1
//创建一个新的元素节点
var a  = docuemnt.createElement("p");
var p = document.createTextNode("hello world")
//把p添加到a节点上去
a.appendChild(a);

##总结一下DOM的常用方法

1
//获取节点
documen.getElementById(id);
document.getElementByTagName(tagName);

//间接引用节点 
//子节点
element.childNode

//返回element的所有子节点,通过element.childNodes[i]的方式来调用
Element.firstChild = element.childNodes[0];
Element.lastChild = element.childNodes[element.childNodes.length;

//父节点
element.parentNode

//下一个兄弟节点
element.nextSibling;

//上一个兄弟节点
element.previousSibling;

//获得节点信息 

//节点名称nodeName,tagName
<a class="test"></a>
document.getElementById('id').nodeName;//a
document.getElementById('id').getAttributes("class").nodeName;//test

//nodeType返回节点的类型。元素节点返回1;属性节点返回2;文本节点返回3;

//nodeValue返回节点的值。元素节点返回null;属性节点返回undefined;文本节点返回文本内容。

//hasChildNodes()返回布尔值,判断是否含有子节点。


//添加属性
document.setAttributeNode()

//获取属性
document.getAttribute()

//创建元素节点
document.createElement()

//创建文本节点
Document.createTextNode();

//注: document.createTextNode(" ");他不会通过html编码,也就是说这里创建的不是空格,而是字符串

//添加子节点 appendChild()。
parentElement.appendChild(childElement);

//插入子节点
node.insertBefore()

//使用replaceChild方法取代子结点。
parentNode.replaceChild(newNode,oldNode); 

//使用cloneNode方法复制结点 ,includeChildren为bool,表示是否复制其子结点 
node.cloneNode(includeChildren); 

//使用removeChild方法删除子结点
parentNode.removeChild(childNode);

#事件

以下所提都是基于DOM的事件,node部分不在讨论之列。

嗯,其实我们完全可以说,脚本化的html的核心就是对于各种事件的处理,而针对事件化的编程也使得我们的javascript能够将其事件驱动的思想体现出来。
如果想知道都有哪些事件可以处理的话,可以参考如下的代码。

1
var log = document.getElementById('event'),
    i = '', 
    out = [];
for (i in window) {
  if ( /^on/.test(i)) { out[out.length] = i; }
}
console.log(out);

嗯,会有一大坨的~可以参考这里

事件处理

添加事件

相对而言处理事件还是比较简单和常见的,正如我们在前面举得很多例子一样,对于事件的处理有两种方式,这里以click事件为例

1
//method one
node.onclick = function(){
    //do some thing
}

//method two
node.addEventLisener("click",function(){
    //do some thing
})

移除事件

如果想移除某一事件,我们应该这么做。

1
var a = function(){
    //do some thing
}

node.addEventLinsener("click",a);
node.removeEventListener("click",a);

注意一下,这里并不可以使用匿名函数了。只能将函数事先申明出来。根据这个机制,我们其实可以弄一个一次性函数,实现起来,也很简单。

1
var a = function(){
    //do some thing
    node.removeEventListener('click',a);
}

node.addEventListener('click',a);

事件的触发过程

嗯,我们知道了怎么处理一个事件,或者说监听一个事件,我来看看事件是怎么被触发的。为了更好理解,我从w3c上偷了一张图
事件流
嗯,更多信息可以看这里
嗯,这里有两个名词,一个是事件冒泡,一个是事件捕捉。对于这个东西,在代码上更直白的表述是,addEventListener函数的第三个参数。

1
node.addEventLisenter("click",callback,false);

第三个参数如果是true,那么就是在捕捉阶段,如果是false,就是在冒泡阶段。

事件捕捉

正如所见,当我们在 DOM 的某个节点发生了一些操作,比如说点击,这个时候浏览器就会从 Window 发出一个事件查询,不断经过下级节点直到目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。

所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。

监听某个在捕获阶段触发的事件,需要在事件监听函数传递第三个参数 true。

1
element.addEventListener(<event-name>, <callback>, true);

事件冒泡

那么,冒泡的话,就是当事件捕捉完成后的回溯过程。嗯,因为IE不支持在事件扑捉阶段对事件进行处理,所以为了统一效果,一般情况下,我们在事件冒泡阶段对事件进行处理。

1
element.addEventListener(<event-name>, <callback>, false);

大家可以通过这个demo来更深入的体会和了解js的事件处理机制。

事件委托

如上所文,其实,对于一个DOM树上的节点而言,他的每一个事件动作,都不只是和他自己有关系,他的每一个事件都会被其父节点给捕捉到。有的时候,我们经常会遇到如下业务场景。
对下面所有的li标签进行点击事件监听。

1
<ul id="id">
    <li>a</li>
    <li>b</li>
    <li>c</li>
    <li>d</li>
    <li>e</li>
    <li>f</li>
    <li>g</li>
    <li>h</li>
    <li>i</li>
    <li>j</li>
    <li>k</li>
    <li>l</li>
    <li>m</li>
    <li>n</li>
    <li>r</li>
</ul>

嗯,如果是往常的话,我们一般会这么写

1
var li = document.getElementById('id').getElementsByTagName('li');
for(var i = 0; i < li.length; i++){
    li[i].addEventListener("click",function(e){
        // do some thing
    },false)
}

但是我们稍微思考一下,就会发现一个问题啊,就是我们这样写事件监听从功能上而言是实现了,但是考虑一下空间复杂度,我们在这里创建了很多其实并不需要的中间变量,其实我们完全可以只在其父元素上进行事件监听,这样一样可以对事件进行处理,但是可以减少很多中间变量。嗯,我们就可以这么写这个代码。

1
var ul = document.getElementById('id');
ul.addEvenListener("click",function(e){
    if(e.target.tagName.toLocaleLowerCase()==="li"){
        //do some thing
    }
},false);

嗯,这样的话,我们一样可以处理ul下面的li的事件,但是可以减少很多我们并不需要的中间变量。提高代码效率。
这里我们用到了event事件的返回值,是一个属性很丰富的对象。详细的东西,看这里就好了。

事件拦截

事件拦截也是很简单事情吗,当我不想让一个事件向上传递的时候,我们可以把它拦截下来。这样他就不会触发后面的事件了。

1
var ul = document.getElementById('id');
ul.addEvenListener("click",function(e){
    if(e.target.tagName.toLocaleLowerCase()==="li"){
        //do some thing
        e.stopPropagation();//阻止事件冒泡
    }
},false);

嗯,这个函数是不是很复杂,对,我们用更简单的。

1
return false;

事件模拟

有的时候,我们想让代码自动触发一些事件,比如说,刷新完自动展开一个本来需要点击展开的div。嗯,这里要用到事件对象。取个例子:

1
function simulateClick() {
  var event = new MouseEvent('click', {
    'view': window,
    'bubbles': true,
    'cancelable': true
  });//新建一个鼠标点击事件。当然,你也可以用document.createEvent来创建。
  var cb = document.getElementById('checkbox'); 
  var canceled = !cb.dispatchEvent(event);//查看事件是否触发了其默认事件
  if (canceled) {
    // A handler called preventDefault.
    alert("canceled");
  } else {
    // None of the handlers called preventDefault.
    alert("not canceled");
  }
}

更多东西,看这里

自定义事件

我们可以自定义事件来实现更灵活的开发,事件用好了可以是一件很强大的工具,基于事件的开发有很多优势(后面介绍)。

与自定义事件的函数有 Event、CustomEvent 和 dispatchEvent。

直接自定义事件,使用 Event 构造函数:

1
var event = new Event('build');

// Listen for the event.
elem.addEventListener('build', function (e) { ... }, false);

// Dispatch the event.
elem.dispatchEvent(event);

CustomEvent 可以创建一个更高度自定义事件,还可以附带一些数据,具体用法如下:

1
var myEvent = new CustomEvent(eventname, options);
其中 options 可以是:

{
    detail: {
        ...
    },
    bubbles: true,
    cancelable: false
}

其中 detail 可以存放一些初始化的信息,可以在触发的时候调用。其他属性就是定义该事件是否具有冒泡等等功能。

内置的事件会由浏览器根据某些操作进行触发,自定义的事件就需要人工触发。dispatchEvent 函数就是用来触发某个事件:

1
element.dispatchEvent(customEvent);

上面代码表示,在 element 上面触发 customEvent 这个事件。结合起来用就是:

1
// add an appropriate event listener
obj.addEventListener("cat", function(e) { process(e.detail) });

// create and dispatch the event
var event = new CustomEvent("cat", {"detail":{"hazcheeseburger":true}});
obj.dispatchEvent(event);

其实呢,我们还可以这样来新建一个事件

1
function func1(){
		console.log("func1 is execute");
	}
	function func2(){
		console.log("func2 is execute");
	}
	var ev=document.createEvent('HTMLEvents');
	ev.initEvent('youevent',false,false);
	document.addEventListener("youevent",func1,false);
	document.addEventListener("youevent",func2,false);

详细内容,参考这个博客
createEvent的方法并不兼容IE,这一点要注意一下。

有兴趣的还可以看看这些博客,有更深的理解
http://unixpapa.com/js/key.html
http://chajn.org/project/javascript-events-responding-user/
http://yujiangshui.com/javascript-event/

文章目录
  1. 1. HTML-DOM
  2. 2. 事件处理
    1. 2.1. 添加事件
    2. 2.2. 移除事件
  3. 3. 事件的触发过程
    1. 3.1. 事件捕捉
    2. 3.2. 事件冒泡
    3. 3.3. 事件委托
    4. 3.4. 事件拦截
    5. 3.5. 事件模拟
    6. 3.6. 自定义事件