JavaScript学习笔记(9)

来源:互联网 发布:淘宝店铺免费模版 编辑:程序博客网 时间:2024/06/10 20:53

DOM

之前已经学了JS的两大核心——ECMAScript和BOM,还剩最后一个核心内容就是DOM(文档对象模型)。关于DOM,原书(《JavaScript高级程序设计(第3版)》)花了足足三章的篇幅来讲解,可见其重要性。


1 基本概念

DOM是针对HTML和XML文档的一个API,描绘了一个层次化的节点数,允许添加、删除、修改页面的某一部分。


2 节点层次

DOM节点分为几大类型,各类型分别表示文档中不同信息和标记,每个节点都有各自的特点、数据、方法,节点之间又相互关联,形成一个树形结构。

<html>    <head>        <title>Sample Page</title>    </head>    <body>        <p>Hello World!</p>    </body></html>

文档节点是每个文档的根节点,上例中文档节点只有一个根节点——<html>元素,称之为文档元素,文档元素是文档唯一的最外层元素,文档中的其他元素都包含其中。
每一段标记都可以通过树的一个节点表示。节点类型共有12种,都继承自一个基类。


2.1 Node类型

JS中所有节点类型都继承自Node。每一个节点都有一个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

通过比较即可确定是什么类型:

if (someNode.nodeType == 1) {    alert("Element node.")}

2.1.1 nodeName和nodeValue属性

这两个属性包含了节点的具体信息,根据不同类型的节点而有所区别,使用之前最好检测一下节点的类型。对于元素节点,nodeName中保存了标签名,而nodeValue始终为null。


2.1.2 节点关系

节点间关系用文档树来描述,有父节点、子节点、兄弟节点等概念和数据结构是相似的。每个节点都有一个childNodes属性,保存了类似数组的NodeList对象,用于保存一组有序的节点。NodeList是一个基于DOM结构动态变化的结果,可以通过someNode.childNodes[index]someNode.childNodes.item(index)访问。

每个节点都有指针指向之前和之后的节点。
节点间关系
这些关系中,childNode属性最为简便,可以用它访问文档树的任意节点,hasChildNode()方法可以检测一个节点是否有子节点。ownerDocument可以直接访问文档节点。


2.1.3 操作节点

下列四个方法是操作子节点的方法,需要先取得父节点(parentNode属性):

方法名 描述 appendChild(newNode) 向childNodes列表的末尾添加一个节点,并返回新增的节点 insertBefore(newNode,referNode) 在referNode之前插入newNode,如果第二个参数为null,则与appendChild作用一样 replaceChild(insertNode,replaceNode) 替换节点,用insertNode替换replaceNode,并删除replaceNode removeChild(Node) 删除指定节点,并返回该节点。

针对所有类型的节点都通用的方法:

方法名 描述 cloneNode(boolean) 创建一个完全相同的副本节点(不包含事件处理程序),true表示深复制,赋值该节点以及整个子节点树,false表示浅复制,仅复制该节点本身。复制得到的节点为孤儿节点,需要用之前的方法将其添加到文档树中 normalize() 处理文档树中的文本节点,删除空文本节点,合并相邻文本节点

2.2 Document类型

Document类型用于表示文档,是非常重要和常用的类型之一。
在浏览器中,document对象是HTMLDocument类型的一个实例,document对象是window对象的一个属性。其特征如下:

  • nodeType为9
  • nodeName为”#document”
  • nodeValue为null
  • parentNode为null
  • ownerDocument为null
  • 子节点可能是DocumentType、Element、ProcessingInstruction、Comment

Document类型可以表示HTML页面或者其他基于XML的文档。document对象是最常见的一个实例。


2.2.1 文档的子节点

可以通过2.1.2的方法访问子节点,还有一些特殊的便捷的访问方法:

属性 描述 document.documentElement 始终指向<html>元素, document.body 始终指向<body>元素, document.doctype 用于访问<!DOCTYPE>(不过支持程度不高)

2.2.2 文档信息

作为HTMLDocument的一个实例,document对象还有一些特殊的属性:

属性 描述 document.title 包含<title>元素中的文本,可以获取和修改title的值 document.URL 取得完整的URL document.domain 取得域名,出于安全考虑,不同子域的页面不能通过JS通信,将两个页面的domain值设置为相同,则可以相互访问。(例如job.bupt.edu.cn和mail.bupt.edu.cn都设置成bupt.edu.cn才能相互通信) document.referrer 取得来源页面的URL

2.2.3 查找元素

方法 描述 getElementById(id) 取得指定id的元素,必须严格匹配,不存在测返回null getElementsByTagName(tag) 返回指定标签的元素所组成的NodeList,HTML中会返回HTMLCollection对象,可以使用下标(数字或name属性)访问其中的节点,参数为*表示返回全部 getElementsByName(name) HTMLDocument特有的方法,返回带有给定name的元素

2.2.4 特殊集合

常用的一些标签,可以用更快速直接的方法得到:

集合 描述 anchors 所有带name特性的<a>元素 forms 所有<form>元素 images 所有<img>元素 links 所有带href特性的<a>元素

2.2.5 DOM一致性检测

DOM有多个级别,浏览器版本也众多,因此检测当前浏览器支持的DOM层级尤为重要,如同浏览器检测一样,DOM检测也有其方法。
document.implementation属性与浏览器的DOM实现直接对应,
hasFeature()方法可以检测DOM功能的名称和版本号,最好配合能力检测。

var hasXmlDom = document.implementation.hasFeature("XML","1.0");//如果返回ture,表示当前浏览器支持XML的1.0版本

2.2.6 文档写入

方法 描述 write(str) 将文本写入输出流,如果加载完成之后再调用会重写整个页面 writeln(str) 将文本写入输出流,末尾添加换行 open()/close() 打开或关闭输入流

2.3 Element类型

该类型用于表现XML或HTML元素,可以访问该元素的标签名、子节点、特性。其特征如下:

  • nodeType为1
  • nodeName为元素的标签名,tagName属性也有相同的效果
  • nodeValue为null
  • parentNode可能为Document或Element
  • 子节点可能是Element、Text、Comment、ProcessingInstruction、CDATASection、EntityReference

HTML中,tagName返回的值是大写,比较之前要注意转换


2.3.1 HTML元素

HTML元素都由HTMLElement类型及其子类型表示。HTMLElement为HTML元素添加了对应于标准特性的属性:

  • id,元素的唯一标识符
  • title,元素我的附加说明
  • lang,语言代码
  • dir,语言的方向(从左至右ltr,从右至左rtl)
  • className,对应于CSS类的class特性

可以访问和修改该属性。

var div = document.getElementById("someDiv");alert(div.id); //访问div.id = "newId"; //修改

2.3.2 操作特性

getAttribute()setAttribute()removeAttribute()用于获取、设置、删除指定的特性。
一般情况下,标准规定的特性(例如:align,href)都可以直接通过2.3.1的方法获取和设置,getAttribute()这类函数更适合处理自定义的特性。

<div id="someDiv" selfdefined_attr="something">something</div><script>    var div = document.getElementById("someDiv");    alert(div.getAttribute("selfdefined_attr"));    div.setAttribute("selfdefined_attr","anothor");    div.removeAttribute("selfdefined_attr");</script>

2.3.3 attributes属性

Element类型是使用attributes属性的唯一一个类型,该属性包含一个类似于NodeList的NamedNodeMap集合,元素的每一个特性都有一个Attr节点表示,每个节点都保存在NamedNodeMap当中。下列方法用于操作NamedNodeMap。

方法 描述 getNameItem(name) 返回nodeName属性等于name的节点 removeNamedItem(name) 从列表中移除nodeName为name的节点 setNamedItem(nade) 向列表中添加节点,以节点的nodeName属性为索引 item(pos) 返回位于属性pos位置处的节点

attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,nodeValue是特性值。

var id = element.attributes.getNamedItem("id").nodeValue;var id = element.attributes["id"].nodeValue;element.attributes["id"].nodeValue = "someOtherId";

通常情况下,用2.3.2的方法会更方便,但在遍历元素属性时,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.attributes[i].nadeValue;        if (element.attributes[i].specified) { //为了适配IE7以下的浏览器            pairs.push(attrName + "=\"" + attrValue + "\"");        }    }    return pairs.join(" ");}

2.3.4 创建元素

document.createElement(tagName)方法用于创建元素,参数为要创建的元素的标签名。

var div = document.createElement("div"); //创建一个<div>元素div.id = "newId"; div.className="box"; document.body.appendChild(div); //将节点加到文档树中,并被立即显示在浏览器上

2.3.5 元素的子节点

元素可以拥有任意数量的子节点和后代节点,元素的childNodes属性包含了其全部子节点(可能是元素节点、文本节点、注释节点等等)。不同的浏览器处理方式也不尽相同,所以操作之前要检查nodeType属性。

for (var i=0,len=element.childNodes.length;i<len;i++) {    if (element.childNodes[i].nodeType == 1){        //do something    }}

2.4 Text类型

Text类型用于表示文本节点,包含的可以是纯文本内容,包括转义后的HTML字符,但不包含HTML代码,其特征如下:

  • nodeType为3
  • nodeName为”#text”
  • nodeValue为节点所包含的文本,data属性效果相同
  • parentNode为一个Element节点
  • 不存在子节点

默认情况下,每个可以包含内容的元素最多有一个文本节点(没有内容就没有文本节点,有内容或空格就会有文本节点)。

相关操作方法:

方法 描述 appendData(text) 将text添加到节点的末尾 deleteData(offset,count) 从offset的位置开始删掉count个字符 insertData(offset,text) 在offset指定的位置插入text replaceData(offset,count,text) 用text替换从offset指定位置开始到offset+count为止处的文本 splitText(offset) 从offset处将节点分成两个文本节点,前一段从0到offset-1,后一段从offset到结尾 substringData(offset,count) 提取从offset指定的位置开始的count个字符组成的字符串 createTextNode(text) 创建文本节点,参数时要插入节点中的文本,创建后要将其添加到文档树中 normalize() 合并调用该方法的元素下所有的文本节点

2.5 Comment类型

用于表示注释,特征如下:

  • nodeType为8
  • nodeName为”#comment”
  • nodeValue为注释内容,data属性效果相同
  • parentNode为一个Element节点或Element节点
  • 不存在子节点

该类型继承自Text类型,与Text类型相比,没有splitText()方法,其余方法都有。


2.6 CDATASection类型

用于表示CDATA内容,特征如下:

  • nodeType为4
  • nodeName为”#cdata-section”
  • nodeValue为CDATA内容
  • parentNode为一个Element节点或Element节点
  • 不存在子节点

该类型继承自Text类型,与Text类型相比,没有splitText()方法,其余方法都有。
CDATA只会出现在XML文档中,大多数浏览器会将其解析为Comment或Element。


2.7 DocumentType类型

包含与文档doctype信息,特征如下:

  • nodeType为10
  • nodeName为doctype的名称
  • nodeValue为null
  • parentNode为一个Document
  • 不存在子节点

2.8 DocumentFragment类型

作用类似于“中转站”,将要添加到文档树中的节点先放入DocumentFragment中处理、整合好之后,再一次性加到文档树中,避免浏览器多次渲染,多次刷新。

  • nodeType为11
  • nodeName为”#document-fragment”
  • nodeValue为null
  • parentNode为null
  • 子节点可以是Element、ProcessingInstruction、Comment、Text、CDATASection、EntityReference

该类型继承了Node的所有方法。

var fragment = document.createDocumentFragment();var ul = document.getElementById("list");var li = null;for (var i=0;i<3;i++) {    li = document.createElement("li");    li.appendChild(document.createTextNode("Item " + (i+1)));    fragment.appendChild(li);}ul.appendChild(fragment);//一次性添加三个节点

2.9 Attr类型

用于访问特性节点,但实际上使用2.3.2的方法会更简便。


3 DOM操作技术

3.1 动态脚本

可以通过操作DOM,动态的添加外部文件。即加载时不存在该脚本,在页面运行过程中通过修改DOM动态添加。

function loadScriptFile(url){    var script = document.createElement("script"); //创建<script>标签    script.src = "some.js";     document.body.appendChild(script);//将script节点插入文档树,作为body的子节点}function loadScript(code){    var script = document.createElement("script"); //创建<script>标签    try {        script.appendChild(document.createTextNode(code)); //将参数(脚本的具体内容)作为子文本节点插入script节点    } catch (ex) {        script.text = code; //兼容IE    }    document.body.appendChild(script);//将script节点插入文档树,作为body的子节点}

执行loadScript(code)函数的效果实际上和eval()是一样的,这种方式加载的代码会在全局作用域中执行。


3.2 动态样式

方法与加载JS文件和代码类似


3.3 动态表格

表格涉及的元素和标签都比较多,但动态创建一个表格是经常要处理的问题。
DOM提供了规范化的属性和方法用于表格的动态创建。

HTML元素 元素的作用 对应的属性和方法 <table> 定义表格 <caption> 定义表格标题 caption:保存指向caption的指针
createCaption():创建caption元素
deleteCaption():删除caption元素 <th> 定义表格的表头 <tr> 定义表格的行 rows:是一个表格中所有行的HTMLCollection
insertRow(pos):在rows集合中插入一行
deleteRow(pos):删除指定的行 <td> 定义表格单元格 cells:保存着tr元素中单元格的HTMLCollection
insertCell(pos):向cells集合中的指定位置插入一个单元格
deleteCell(pos):删除指定的单元格 <thead> 定义表格的页眉 tHead:保存指向thead的指针
createTHead():创建thead元素
deleteTHead():删除thead元素 <tbody> 定义表格的主体 tBodies:是tbody元素的HTMLCollection <tfoot> 定义表格的页脚 tFoot:保存指向tfoot的指针
createTFoot():创建tfoot元素
deleteTFoot():删除tfoot元素 <col> 定义用于表格列的属性 <colgroup> 定义表格列的组

举个例子

var table = document.createElement("table");var caption = table.createCaption("title");var thead = table.createTHead("head");var tbody = document.createElement("tbody");var tfoot = table.createTFoot("foot");table.appendChild(caption);table.appendChild(thead);table.appendChild(tbody);table.appendChild(tfoot);tbody.insertRow(0);tbody.rows[0].insertCell(0);tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1"));tbody.rows[0].insertCell(1);tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2"));tbody.insertRow(1);tbody.rows[1].insertCell(0);tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 3"));tbody.rows[1].insertCell(1);tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 4"));document.body.appendChild(table);

3.4 NodeList

DOM中NodeList、NamedNodeMap、HTMLCollection是三个动态的集合,每当文档结构发生变化,就会更新这个集合。本质上,每次访问NodeList对象时都会实时查询一次DOM树。当想要迭代一个NodeList时,最好先将要使用的相关值保存下来。

var divs = document.getElementsByTagName("div"), i, len, div;for(i=0,len=divs.length; i<len; i++) {    div = document.createElement("div");    document.body.appendChild(div);}
0 0
原创粉丝点击