DOM 文件对象模型
节点层次
DOM 可以将 HTML 或 XML 描绘成一个由多层节点构成的结构。
文档节点是每个文档的根结点:
<!-- 在这个例子中,<html> 是文档节点,或者称为文档元素 -->
<html>
<head>
<title>Sample Page</html>
</head>
<body>
<p>Hello World!</p>
</body>
</html>
Node 类型
JS 中所有节点类型都继承自 Node 类型,每个节点都有 nodeType 属性,用于标明节点的类型,常用的有:
Node.ELEMENT_NODE(常数值为 1 )Node.TEXT_NODE(常数值为 3 )
其余的节点类型见书 P248 。
考虑浏览器兼容,建议用常数值来判断节点类型:
if (someNode.nodeType == 1) {
alert("Node is an element.")
}
可以访问 nodeName 和 nodeValue 属性获得具体的节点信息,但访问之前最好检查节点类型:
// 判断是不是元素节点
if (someNode.nodeType == 1) {
// 在这里 nodeName 始终是元素的标签名
name = someNode.nodeName
// 而 nodeValue 始终为 null
value = someNode.nodeValue
}
可以通过 childNodes 属性访问一个节点的子节点,且为一个 NodeList 对象(不是 Array ):
// 既可以用下标,也可以用 item 方法来获得子节点
var firstChild = someNode.childNodes[0]
var secondChild = someNode.childNodes.item(1)
// 获得子节点个数
var count = someNode.childNodes.length
将 NodeList 对象转为数组:
// 转为数组
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes, 0)
// 或者这样转为数组
var array = new Array()
for (var i = 0, len = nodes.length; i < len; i += 1) {
array.push(nodes[i])
}
别的访问节点的方式:
可以通过
parentNode属性访问父节点通过
previousSibling和nextSibling访问兄弟节点通过
firstChild和lastChild访问第一个和最后一个子节点
可以见书 P251 查看关系图。
可以通过 ownerDocument 访问整个文档的文档节点。
节点操作:
appendChild方法在末端添加节点,如果是文档的一部分则为位置的变化insertBefore(newNode, node)将newNode插入到node之前,node可以为null表示在末端插节点replace(newNode, node)替换节点,被替换的节点不会消失,只是没有位置removeChild(node)删除节点
可以用 cloneNode(flag) 来复制节点。flag 为 true 时复制整棵节点树;false 只复制本节点,没有父亲和儿子。cloneNode 方法不会复制 DOM 节点中的 JS 属性。
Document 类型
JS 用 Document 类型表示文档。在浏览器中,document 对象是 HTMLDocument(继承 Document )的一个实例。同时,document 是 window 的一个属性,所以可以全局访问。
Document 节点的属性值:
nodeType为 9nodeName为"#document"nodeValue为nullparentNode为nullownerDocument为null
快速访问节点:
documentElement属性访问document中的<html>元素body属性访问<body>元素doctype访问<!DOCTYPE>元素(慎用)
文档信息:
title访问<title>元素,可以直接设置 title 属性值为新标题名URL返回地址栏显示的 URLdomain返回域名。如果有子域名,则不可以设置新域名referer返回来源页面的 URL
查找元素:
getElementById(id)返回对应id属性的元素,严格匹配大小写建议 HTML 中不存在
name属性和id属性值一致的情况文档中可能会有多个同
id的元素,但只会返回第一个
getElementsByTagName(tag)返回对应标签的HTMLCollection(和NodeList类似)- 可以通过
namedItem(name)来获取对应name属性的项,这里的name不区分大小写,当name为 "*" 时可以匹配所有项
- 可以通过
getElementsByName(name)返回对应name属性的元素的NodeList:<fieldset>
<legend>Which color do you prefer?</legend>
<ul>
<li>
<input type="radio" value="red" name="color" id="colorRed">
<!-- label 会去对应一个 input -->
<label for="colorRed">Red</label>
</li>
<li>
<input type="radio" value="green" name="color" id="colorGreen">
<label for="colorGreen">Green</label>
</li>
<li>
<input type="radio" value="blue" name="color" id="colorBlue">
<label for="colorBlue">Blue</label>
</li>
</ul>
</fieldset>可以这样获取所有
name属性为"color"的元素,即所有单选按钮:var radios = document.getElementsByName("color")
特殊集合:
anchors属性返回带name属性的<a>元素forms属性返回<form>元素images属性返回<img>元素links属性返回带href属性的<a>元素
可以用
document.implementation.hasFeature(func, ver)来检测 DOM 实现(慎用,具体见书 P259 )。
文档写入:
write()方法直接在 HTML 中写入内容,注意转义上的严谨问题writeln()方法会在write的基础上加"\n"open()和close()用于打开和关闭网页的输出流,一般用不到
Element 类型
Element 是 Web 编程中最常用的类型,具有以下特征:
nodeType为 1nodeName为元素的标签名,且返回值为大写,也可以用tagName属性访问nodeValue为null
HTML 元素:
id返回 id 属性title返回 title 属性className返回 class 属性
还有
lang和dir属性,见书 P262 。
各种 HTML 元素实现类型见书 P263 。
也可以通过 getAttribute(name) 获取属性:
var div = document.getElementById("myDiv")
alert(div.getAttribute("id")) // "myDiv"
根据 HTML5 规范,自定义属性应当加上 "data-" 前缀以便验证
有两个特殊属性:
style用
getAttribute()访问 style 属性会获得一个 CSS 文本。直接访问 style 属性,会返回一个对象。
onclick
用
getAttribute()访问 onclick 属性会获得一个 js 文本。直接访问 style 属性,会返回一个函数。
设置属性用 setAttribute(name, value) ,不管是已有的还是未有的属性:
div.setAttribute("id", "someOtherId")
用 removeAttribute(name) 移除属性
Element 类型有 attributes 属性,其类型为 NamedNodeMap ,和 NodeList 类似,具体使用见书 P266 。
用 document.createElement(type) 可以创建新元素:
// 此时 div 设置了 ownerDocument ,但没有添加到文档树中
var div = document.createElement("div")
div.id = "myNewDiv"
// 记得添加节点
document.body.appendChild(div)
Text 类型
nodeType为 3nodeName为"#text"nodeValue为节点所包含的文本,也可以通过 data 属性访问parentNode为一个Element
设置文本:
appendData(text)deleteData(offset, count)insertData(offset, text)replaceData(offset, count, text)splitText(offset, count)将文本节点分裂为两个文本节点,同时返回后一个文本节点,可见 Text.splitText()substringData(offset, count)
一般情况下,一个包含内容的元素只能拥有一个文本节点
可以用 document.createTextNode(text) 来创建文本节点:
// 创建元素节点
var element = document.createElement("div")
element.className = "message"
// 创建文本节点
var textNode = document.createTextNode("Hello world!")
// 将 textNode 添加到 element 中
element.appendChild(textNode)
// 将 element 添加到 body 中
document.body.appendChild(element)
如果一个元素有多个文本节点,可以用 nomalize 方法来合并这些节点,和 splitText 操作相反。
Comment 类型
nodeType为 8nodeName为"#comment"nodeValue为注释的内容
Comment 和 Text 继承自相同的基类,所以拥有除 splitText 外的字符串属性及方法:
<div id="myDiv"><!--A comment--></div>
var div = document.getElementById("myDiv")
var comment = div.firstChild
alert(comment.data) // "A comment"
注意,只有文档节点下的注释才能被获得,同时可以用 document.createComment() 来创建注释节点(很少用)。
其他类型
CDATASection 类型
只针对 XML 文档,见书 P274 。
DocumentType 类型
处理 doctype 标签,见书 P274 。
DocumentFragment 类型
这类节点不会出现在 HTML 中,也不能添加到文档树,但它可以包含和控制节点。
可以用 document.createDocumentFragment 方法来创建。
当别的节点用 appendChild 等方法添加文档片段时,会将其所有子节点添加到文档中:
<ul id="myList"></ul>
var fragment = document.createDocumentFragment()
var ul = document.getElementById("myList")
var li = null
for (var i = 0; i < 3; i += 1) {
li = document.createElement("li")
li.appendChild(document.createTextNode("Item " + (i + 1)))
fragment.appendChild(li)
}
// 此时 ul 会多出三个 li
ul.appendChild(fragment)
Attr 类型
属性其实也是节点,但不被认为文档树的一部分。开发人员常用 getAttribute() 等方法,很少直接引用节点,具体见书 P277 。
这里给一个创建属性节点的样例:
var attr = document.createAttribute("align")
attr.value = "left"
element.setAttributeNode(attr)
alert(element.attributes["align"].value) // left
alert(element.getAttributeNode("align").value) // left
alert(element.getAttribute("align")) // left
DOM 操作技术
可以这样动态地植入一个脚本:
function loadScript(url) {
var script = document.createElement("script")
script.type = "text/javascript"
script.src = url
document.body.appendChild(script)
}
和动态脚本类似,可以这样动态的植入一个样式:
function loadStyles(url) {
var link = document.createElement("link")
link.rel = "stylesheet"
link.type = "text/css"
link.href = url
var head = document.getElementsByTagName("head")[0]
head.appendChild(link)
}
表格相关的属性及操作见书 P281,这里给出一个样例:
var table = document.createElement("table")
table.border = 1
table.width = "100%"
var tbody = document.createElement("tbody")
table.appendChild(tbody)
// 创建第一行
tbody.insertRow(0)
tbody.rows[0].insertCell(0)
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"))
tbody.rows[0].insertCell(1)
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 2,1"))
// 创建第二行
tbody.insertRow(1)
tbody.rows[0].insertCell(0)
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,2"))
tbody.rows[0].insertCell(1)
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 2,2"))
其实现为:
<table border="1" width="100%">
<tbody>
<tr>
<td>Cell 1,1</td>
<td>Cell 2,1</td>
</tr>
<tr>
<td>Cell 1,2</td>
<td>Cell 2,2</td>
</tr>
</tbody>
</table>