事件就是用户或浏览器自身执行的某种动作,如click,laod,mouseover都是事件的名称。
事件流描述的是从页面中接收事件的顺序。
事件处理程序就是对事件作出响应的函数。事件处理程序的名字以“on”开头,如click事件对应的事件处理程序的名称为onclick。
为事件指定处理程序的方式有多种,如:HTML事件处理程序、DMO0级事件处理程序、DOM2级事件处理程序、IE事件处理程序、跨浏览器事件处理程序。
(1)html事件处理程序
即:将事件处理程序,写在相应的html标签中。
eg:
<input type="button" value="click me" onclick="alert("hello")" />
缺点:①存在一个时间差,当用户在html元素一出现在页面上就去触发相应的事件时,事件的处理程序可能还不具备执行条件(比如说调用的函数还木有被解析),就会引发错误。eg:<input type="button" value="click me" onclick="message()" />
<script type="text/javascript">function message(){alert("hello world");}</script>
因为调用的函数处于按钮的下方,如果在message函数被加载之前就点击了按钮就会引发错误。
②html和js代码耦合度太高,如果要改变事件处理程序,就要改动两个地方:html代码和javascript代码。
(2)DMO0级事件处理程序
eg:
var btn=document.getElementById("myBtn");
btn.onclick=function(){alert(this.id)};
注意:如果这段代码位于按钮之后,就有可能在一点时间内怎么点击都木有反应,因为在这段代码运行以前不会指定事件处理程序。
DMO0级事件处理程序被认为是元素的方法,换句话说,DMO0级事件处理程序是在元素的作用域中运行的,所以程序中的this引用当前元素。可以在事件处理程序中通过this访问元素的任何属性和方法。
以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。
也可删除指定的事件处理程序,只要将事件处理程序的属性设置为null就Ok了。
eg:
btn.onclick=null; //删除指定的事件处理程序,将处理程序设置为null以后,再点击按钮不会发生任何动作。
(3)DOM2级事件处理程序
DOM2级事件定义了两个方法,用于指定和删除事件处理程序。这两个操作分别为:addEventListener()和removeEventListner().所有的DOM节点都包含这两个方法。他们要接受3个参数,分别为:要处理的事件名,处理函数,布尔值【第三个参 数是useCapture, 一个bool类型。当为false时为冒泡获取(由里向外),true为capture方式(由外向里)】。
例如在按钮上为click添加事件处理程序,可以用下面的代码:
var btn=document.getElementById("myBtn");
btn.addEventListner("onclick",function(){alert("hello world");false});这里添加的事件处理程序也是依附于元素的的作用域
使用DOM2事件处理程序的优点是:可以为同一个元素添加多个事件处理程序。
例:var btn=getElementById("myBtn");
btn.addEventListner("click",function(){alert(this.id);},flase);
btn.addEventListner("click",function(){alert("hello world");},flase);
结果:先显示id,后显示hello world。
通过addEventListner()添加的事件处理程序只能通过removeEventListner来删除。移除时使用的参数与添加事件处理程序的参数相同。另:通过addEventListner添加的匿名函数无法删除。
(4)IE事件处理程序。
IE添加和删除事件处理程序的函数分别为:attachEvent()和detachEvent();这两个函数接收相同的两个参数:事件处理程序名与事件处理函数。由于IE只支持事件冒泡,所以通过attachEvent添加的事件处理程序都会添加到冒泡阶段。
例如:var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){alert("hello world");});
IE在使用attachEvent方法的情况下,事件处理程序的作用域为全局作用域,因此this等于window。(在编写跨浏览器的代码时,记住这一点非常重要)。
与addEventListner类似,attachEvent()方法也可以用来为一个元素添加多个事件处理程序;
eg:
var btn=document.getElementById("myBtn");
btn.attachEvent("onclick",function(){alert("clicked");});
btn.attachEvent("onclick",function(){alert("hello world");});
值得注意的是:这些事件的处理程序是按逆序触发的,也就是说,先弹出hello world 再弹出clicked。
detach()使用方法略。
(5)跨浏览器的事件处理程序
为了以跨浏览器的方式处理事件,主要可以使用两个方法:
①使用能隔离浏览器差异的js库。
②自己编写最适合的事件处理方法。(即自定义兼容性强的事件函数模型-EventUtil)
这里要用到能力检测,即:识别浏览器的能力。要保证代码能在大多数浏览器下一致的运行,只须关注冒泡阶段。
代码步骤如下:首先要创建的方法是addHandler(用于处理跨浏览器的兼容性问题,这里没有给出具体代码),它的职责是视情况判定使用DOM0级方法,DOM2级方法,IE方法来添加事件。addHandler接收3个参数:要操作的元素、事件名称、事件处理程序函数。这个方法属于一个名叫EventUtil的对象。这里使用这个对象来处理浏览器之间的差异。
与addHandler对应的方法是removeHandler(),它也接受相同的参数。这个事件的职责是移除之前添加的事件处理程序。不论事件是以什么方式添加到对象中的,如果其他方法程序无效,则默认使用DOM0级方法。
使用EventUtil的方法如下:
var btn=document.getElementById("myBtn");
var hander=function(){alert("hello")};//事件处理程序
EventUtil.addHandler(btn,"onclick",handler);
//其他代码
EventUtil.removeHandler(btn,"onclick",handler);
addHandler()和removeHandler()没有考虑到所有的浏览器问题,例如IE中作用域的问题,但是使用它们添加和移除事件处理程序还是足够了。
总结:
-
- var eventHandler = {
- addHandler:function (element, type, func){
- if(element.addEventListener) {
- element.addEventListener(type, func);
- }else if (element.detachEvent){
- element.attachEvent('on' + type, func);
- }else{
- element['on' + type] = func;
- }
- },
- removerHandler:function (element, type, func) {
- if (element.removeEventListener) {
- element.removeEventListener(type, func);
- } else if (element.detachEvent) {
- element.detachEvent('on' + type, func);
- } else {
- element['on' + type] = null;
- }
- }
- };
扩展:
javascript DOM详解之DOM1
DOM1级定义了一个Node接口,该接口将由DOM中所有的节点类型实现。这个Node接口在Javascript中是作为Node类型实现的;除了IE外,其它所有浏览器中都可以访问这个类型。每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在Node类型中定义的下列12个数值常量来表示,任何节点必居其一:
节点数值常量 |
节点常量名称 |
1 |
Node.ELEMENT_NODE |
2 |
Node. ATTRIBUTE_NODE |
3 |
Node. TEXT_NODE |
4 |
Node. CDATA_SECTION_NODE |
5 |
Node. ENTITY_REFERENCE_NODE |
6 |
Node. ENTITY_NODE |
7 |
Node. PROCESSING_INSTRUCTION_NODE |
8 |
Node. COMMENT_NODE |
9 |
Node. DOCUMENT_NODE |
10 |
Node. DOCUMENT_TYPE_NODE |
11 |
Node. DOCUMENT_FRAGMENT_NODE |
12 |
Node. NOTATION_NODE |
由于IE不支持节点常量名称,所以为了保证浏览器的兼容,通常使用节点数值常量进行比较。
每个节点都支持的属性和方法
1.nodeName和nodeValue属性
要了解节点信息,可以使用这两个属性。但是在使用之前,要检测节点的类型
var value;
if(someNode.nodeType==1){
value=someNode.nodeName;
}
2.节点关系
节点之间的关系类似一种数组结构,但是他们之间存在着顺序。使用DOM获取NodeList,这个NodeList是DOM的动态的结果,真实的反应当前的Node状态。NodeList具有length属性。IE8以及更早的版本不支持NodeList,想要做到兼容,可以使用下面的代码进行兼容:
function convertToArray(nodes){
var array=null;
try{
array=Array.prototype.slice.call(nodes,0);//针对非IE
}catch(ex){
array=new Array();
for(var i=0,len=nodes.length;I<len;i++){
array.push(nodes[i]);
}
}
return array;
}
每个节点都有parentNode属性,该属性指向文档书中的父节点。在childNodes中,nextSibling指向后一个节点,previousSibling指向前一个节点。当指向节点不存在时,值为null。
3.操作节点
方法 |
描述 |
createAttribute() |
用指定的名字创建新的Attr节点。 |
createComment() |
用指定的字符串创建新的Comment节点。 |
createElement() |
用指定的标记名创建新的Element节点。 |
createTextNode() |
用指定的文本创建新的TextNode节点。 |
getElementById() |
返回文档中具有指定id属性的Element节点。 |
getElementsByTagName() |
返回文档中具有指定标记名的所有Element节点。 |
方法 |
描述 |
appendChild() |
通过把一个节点增加到当前节点的childNodes[]组,给文档树增加节点。 |
cloneNode() |
复制当前节点,或者复制当前节点以及它的所有子孙节点。 |
hasChildNodes() |
如果当前节点拥有子节点,则将返回true。 |
insertBefore() |
给文档树插入一个节点,位置在当前节点的指定子节点之前。如果该节点已经存在,则删除之再插入到它的位置。 |
removeChild() |
从文档树中删除并返回指定的子节点。 |
replaceChild() |
从文档树中删除并返回指定的子节点,用另一个节点替换它。 |
其中这些方法相当的常用,需要熟记。
在操作子节点的方法中,它们操作后都是返回不用的节点或者是正在被操作的节点。
cloneNode(true),实现的是深层复制,子节点也会复制;cloneNode(false),实现的是浅层复制,子节点不会复制。normalize():处理文档树中的文本节点。
Document类型
js通过Document类型表示文档。在浏览器中,document对象是HTMLDocument的一个实例,表示整个页面。而且document对象是window对象的一个属性,可以将其作为全局对象来访问。
特征:nodeName="#document" nodeType=9
1.文档的子节点
documentElement始终指向页面的html部分,它要比childNodes速度要快。由于页面中body属性比较常用,因此document.body就是对页面上body的快捷引用。document另一个可能的子节点DocumentType可以通过docuemnt.doctype来获取。
2.文档信息
document.title,获取页面上title标签的文本内容。document.url包含着完整的URL,document.domain只包含着页面的域名。document.referrer则保存着连接到当前页面的那个页面的URL。如果没有页面来源,referrer是空字符串。document.domain设置可以解决跨域问题,但是它一旦设置将会无法再次被更改。
3.查找元素
document.getElementById(),参数为domNode的id,但是它只返回第一次出现这个id的domNode。document.getElementByTagName(),参数为要取得元素的标签名,返回零个或者多个元素的NodeList。这个NodeList对象可以使用方括号语法或item()方法来访问HTMLCollection对象中的项。HTMLCollection对象还有以一个方法叫做namedItem(),使用这个方法可以通过元素的name属性取得集合中的项。当document.getElementsByTagName()的参数为"*"时,表示获取页面的全部元素。
document.getElementsByName(),参数为节点的name属性,结果类似document.getElementByTagName()。
4.特殊集合
document.anchors;document.forms;document.images;document.links
5DOM一致性检测
检测实例
var hasXmlDom=document.implementation.hasFeature("XML","1.0");
但是这个检测有时并不可靠,需要进行能力检测。
6文档的写入
write(),writeln(),open(),close()是操作文档写入相关的函数。
Element类型
nodeName为元素的标签名,nodeValue为null。
要访问元素的标签名,可以使用tagName获取,获取到的标签名很大的可能性为大写,比较的时候,要进行转化才可以比较。
1.一般情况下具有的属性:id,title,lang(语言),dir(语言的方向,左右朝向,上下朝向),className(样式)
2.取得属性的方法:getAttribute(),setAttribute(),removeAttribute().属性名称不区分大小写,HTML5的自定义属性前面要加data-前缀以方便验证。style属性返回的是CSS中的第一个对象,它是为了方便编程方式访问使用的。js的事件处理程序,通过getAttribute()获取,只会得到第一个处理函数。由于IE的兼容问题,getAttribute()使用频率降低了不少,一般用对象.属性的方式获取属性值。
3.Element类型是使用attributes属性的唯一DOM节点类型。attributes属性包含了一个NameNodeMap,与NodeList类似,也是一个动态集合。每个属性都有一个Attr节点表示,每个节点保存在NameNodeMap中。NameNodeMap对象具有以下方法:
getNamedItem(name):返回nodeName属性等于name的节点
removeNamedItem(name):从列表中移除以节点nodeName属性等于name的节点
setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引
item(pos):返回位于数字pos位置处的节点
var id=element.attributes.getNamedItem("id").nodeValue;
var id=element.attributes["id"].nodeValue;
如果想遍历元素的特性,attributes属性可以派上用场。
function outputAttributes(element){
var pairs=new Array(),attrName,attrValue,i,len;
for(i=0;len=element.attributes.length;i<len;i++){
attrName=element.attributes[i].nodeName;
attrValue=element.attribute[i].nodeValue;
if(element.attribute[i].specified){
pairs.push(attrName+"=\""+attrValue+"\"");
}
}
return pairs.join(" ");
}
4创建元素
document.createElement(),参数为标签名。但是IE中也可以使用下面的方法
var div=document.createElement("<div id=\"mydiv\" class=\"box\"/>");
这种方式有助于规避IE7以前的动态创建问题:
1.不能设置动态创建的<iframe>元素的name
2.不能通过表单的reset()方法重设动态创建的input元素
3.动态创建的type特性值为“reset”的按钮重设不了表单
4.动态创建的一批name相同的单选按钮彼此毫无关系
5.元素的子节点
var items=document.getElementById("mydiv").getElementsByTagName("li");
Text类型
nodeName="#text",nodeValue的值为节点所包含的文本。data的值为nodeValue的值是一样的,两个等价。
appendData(text):将text添加到节点的尾部。
deleteData(offset,count):从offset处开始删除,长度为count
insertData(offset,text):在offset指定的位置插入text
replaceData(offset,count,text):用text替换从offset位置开始长度为count的文本
splitText(offset):从offset处将文本分成两个文本节点
substringData(offset,count):提取从offset开始,长度为count的文本
length属性,保存文本的长度
创建文本节点的方法:document.createTextNode(),参数为要插入的文本。在创建新的文本节点的同时,也会设置ownerDocument属性。创建示例:
var textNode=document.createTextNode("<strong>Hello</strong>world!");
规范化文本节点使用的方法是normalize(),它会将两个相邻的文本节点进行合并。使用的时候调用相邻文本节点的父节点的normalize()方法,会合并文本节点。
分割文本节点splitText(offset),操作的节点为文本节点,即TextNode.splitText(offset);
Comment类型
它表示在DOM中的注释节点。nodeName=“#comment”;nodeValue值是注释的内容,同TextNode类似,data也可以取得注释的内容。
CDATASection类型
这个类型只是针对基于XML的文档,表示的是CDATA区域。由于这种类型继承自Text类型,因此除了splitText()之外所有的字符串操作方法,它还具有一下的特征和方法:
nodeName="#cdata-selection";nodeValue是CDATA区域中的内容。
只有真正的XML文档才可以使用document.createCDataSelection()来创建CDATA区域,只需要传入节点内容即可。
DocumentType类型
在web浏览器中并不常用,仅有FireFox,Safari和Opera支持。它包含着与文档doctype相关的所有信息。
nodeName的值是doctype的名称,nodeValue是null。在DOM1中,DocumentType对象不能动态的创建,而只能通过解析代码的方式来创建。它有三个属性,name:表示文档的名称;entities:由文档类型描述的实体的NamedNodeMap对象,notations:由文档类型描述的符号的NamedNodeMap对象。
DocumentFragment类型
DOM规定文档片段(document fragment)是一种轻量级的文档,可以包含和控制节点,但是不会像完整的文档那样占用额外的资源。nodeName="#document-fragment",nodeValue值为null。虽然不能把文档片段直接添加到文档当中,但是可以将它作为一个仓库来使用,即里面可以保存一些可能会添加到文档中的节点。创建方法:
var fragment=document.createDocumentFragment();
文档片段可以采用document类似的对子节点的操作,但是把文档片段当做子节点添加到文档树中的时候,只是将文档片段的子节点添加到文档树中,fragment不会添加到DOM中,但是fragment会清空,它本身永远不会成文档树的一部分。
Attr类型
它是DOM中attributes的节点,nodeName的值是属性的名称,nodeValue是属性的值。它有三个相关属性name,value,specified是一个布尔值,以区别是代码指定的Attr还是默认的。
DOM操作技术
动态加载js文件,css文件,动态生成表格.
NodeList,NamedNodeMap,HTMLCollection是DOM的实际状况的反应,是整体上把握DOM的关键。