每日三问之javascript—DOM文档对象模型

DOM (文档对象模型)是针对 HTML XML 文档的一个 API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。

节点层次

以下面的 HTML 为例:
<html> 
 <head> 
 <title>Sample Page</title> 
 </head> 
 <body> 
 <p>Hello World!</p> 
 </body> 
</html>
文档节点是每个文档的根节点。在这个例子中,文档节点只有一个子节点,即 <html>元素,我们称之为 文档元素
HTML 页面中,文档元素始终都是 <html> 元素。在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素。

Node类型

JavaScript 中的所有节点类型都继承自 Node 类型,因此所有节点类型都共享着相同的基本属性和方法。

 每个节点都有一个 nodeType 属性,用于表明节点的类型。

if (someNode.nodeType == Node.ELEMENT_NODE){ //在 IE 中无效
 alert("Node is an element."); 
}
如果二者相等,则意味着someNode 确实是一个元素。
由于 IE 没有公开 Node 类型的构造函数,为了确保跨浏览器兼容,最好还是将 nodeType 属性与数字值进行比较。
if (someNode.nodeType == 1){ //适用于所有浏览器
 alert("Node is an element."); 
}

nodeName nodeValue 属性 

要了解节点的具体信息,可以使用 nodeName nodeValue 这两个属性。
if (someNode.nodeType == 1){ 
 value = someNode.nodeName; //nodeName 的值是元素的标签名
}
在这个例子中,首先检查节点类型,看它是不是一个元素。如果是,则取得并保存 nodeName 的值。对于元素节点,nodeName 中保存的始终都是元素的标签名,而 nodeValue 的值则始终为 null
节点关系
每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。 NodeList 是一种类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。
下面的例子展示了如何访问保存在 NodeList 中的节点——可以通过方括号,也可以使用 item()方法。
var firstChild = someNode.childNodes[0]; 
var secondChild = someNode.childNodes.item(1); 
var count = someNode.childNodes.length;
我们在本书前面介绍过,对 arguments 对象使用 Array.prototype.slice() 方法可以将其转换为数组。
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; 
}

// 备注:

这个 convertToArray()函数首先尝试了创建数组的最简单方式。如果导致了错误(说明是在
IE8 及更早版本中),则通过 try-catch 块来捕获错误,然后手动创建数组。
每个节点都有一个 parentNode 属性,该属性指向文档树中的父节点。

另外, hasChildNodes() 也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回 true
操作节点

appendChild

最常用的方法是appendChild(),用于向 childNodes 列表的末尾添加一个节点。
var returnedNode = someNode.appendChild(newNode); 
alert(returnedNode == newNode); //true 
alert(someNode.lastChild == newNode); //true
如果在调用 appendChild() 时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点,如下面的例子所示。
//someNode 有多个子节点
var returnedNode = someNode.appendChild(someNode.firstChild); 
alert(returnedNode == someNode.firstChild); //false 
alert(returnedNode == someNode.lastChild); //true

insertBefore 

如果需要把节点放在 childNodes 列表中某个特定的位置上,而不是放在末尾,那么可以使用 insertBefore()方法。这个方法接受两个参数:要插入的节点和作为参照的节点。
//插入后成为最后一个子节点
returnedNode = someNode.insertBefore(newNode, null); 
alert(newNode == someNode.lastChild); //true 
//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild); 
alert(returnedNode == newNode); //true 
alert(newNode == someNode.firstChild); //true 
//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild); 
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true

replaceChild 

replaceChild()方法接受的两个参数是:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中被移除,同时由要插入的节点占据其位置。

//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild); 
//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

removeChild

如果只想移除而非替换节点,可以使用 removeChild() 方法。这个方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值。
//移除第一个子节点
var formerFirstChild = someNode.removeChild(someNode.firstChild); 
//移除最后一个子节点
var formerLastChild = someNode.removeChild(someNode.lastChild);
其他方法

cloneNode

cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本。接受一个布尔值参数,表示是否执行深复制。true表示执行深复制,false表示浅复制。
<ul> 
 <li>item 1</li> 
 <li>item 2</li> 
 <li>item 3</li> 
</ul>

// 深复制
var deepList = myList.cloneNode(true); 
alert(deepList.childNodes.length); //3(IE < 9)或 7(其他浏览器)

// 浅复制
var shallowList = myList.cloneNode(false); 
alert(shallowList.childNodes.length); //0

Document类型

文档的子节点

在浏览器中, document 对象是 HTMLDocument (继承自 Document 类型)的一个实例,表示整个 HTML 页面。而且, document 对象是 window 对象的一个属性,因此可以将其作为全局对象来访问。
Document 节点具有下列特征:
​​​
  • nodeType 的值为 9
  • nodeName 的值为 "#document"
  • 其子节点可能是一个 DocumentType (最多一个)、 Element (最多一个)、 ProcessingInstruction或 Comment
  • ownerDocument 的值为 null
  • parentNode 的值为 null
  • nodeValue 的值为 null
两个内置的访问其子节点的快捷方式:
  • 第一个就是 documentElement属性,该属性始终指向 HTML 页面中的<html>元素。
  • 另一个就是通过 childNodes 列表访问文档元素

documentElement

但通过 documentElement 属性则能更快捷、更直接地访问该元素
<html> 
 <body> 
 
 </body> 
</html>

var html = document.documentElement; //取得对<html>的引用
alert(html === document.childNodes[0]); //true 
alert(html === document.firstChild); //true

body

作为 HTMLDocument 的实例, document 对象还有一个 body 属性,直接指向 <body> 元素。
var body = document.body; //取得对<body>的引用

 DocumentType

 Document 另一个可能的子节点是 DocumentType。

var doctype = document.doctype; //取得对<!DOCTYPE>的引用

 文档信息

title

//取得文档标题
var originalTitle = document.title; 
//设置文档标题
document.title = "New page title";

URLdomain和 referrer

//取得完整的 URL
var url = document.URL; 
//取得域名
var domain = document.domain; 
//取得来源页面的 URL
var referrer = document.referrer;
查找元素

getElementById

第一个方法, getElementById() ,接收一个参数:要取得的元素的 ID
<div id="myDiv">Some text</div> 
// 可以使用下面的代码取得这个元素:
var div = document.getElementById("myDiv"); //取得<div>元素的引用

getElementsByTagName

这个方法接受一个参数,即要取得元素的标签名,而返回的是包含零或多个元素的 NodeList
var images = document.getElementsByTagName("img");

alert(images.length); //输出图像的数量
alert(images[0].src); //输出第一个图像元素的 src 特性
alert(images.item(0).src); //输出第一个图像元素的 src 特性

 namedItem

使用这个方法可以通过元素的 name特性取得集合中的项。
<img src="myimage.gif" name="myImage">

var myImage = images.namedItem("myImage");
要想取得文档中的所有元素,可以向 getElementsByTagName() 中传入 "*"
var allElements = document.getElementsByTagName("*");

getElementsByName

这个方法会返回带有给定 name 特性的所有元素。
<fieldset> 
 <legend>Which color do you prefer?</legend> 
 <ul> 
 <li><input type="radio" value="red" name="color" id="colorRed"> 
 <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>

var radios = document.getElementsByName("color");

 特殊集合

  • document.anchors,包含文档中所有带 name 特性的<a>元素;
  • document.forms ,包含文档中所有的 <form> 元素 document.getElementsByTagName("form")
    得到的结果相同;
  • document.links ,包含文档中所有带 href 特性的 <a> 元素。
  • document.images ,包含文档中所有的 <img> 元素,与 document.getElementsByTagName
    ("img") 得到的结果相同;

Element类型

Element 节点具有以下特征:
  • nodeType 的值为 1
  • nodeName 的值为元素的标签名;
  • nodeValue 的值为 null
  • parentNode 可能是 Document Element
  • 其子节点可能是 ElementTextCommentProcessingInstructionCDATASection EntityReference
要访问元素的标签名,可以使用 nodeName 属性,也可以使用 tagName 属性;
<div id="myDiv"></div>

//可以像下面这样取得这个元素及其标签名:
var div = document.getElementById("myDiv"); 
alert(div.tagName); //"DIV" 
alert(div.tagName == div.nodeName); //true
div.tagName 实际上输出的是"DIV"而非 "div" 。在 HTML 中,标签名始终都以全部大写表示;
if (element.tagName.toLowerCase() == "div"){ //这样最好(适用于任何文档)
 //在此执行某些操作
}

操作特性的DOM 方法主要有三个,分别是 getAttribute()setAttribute()removeAttribute()

getAttribute

取得特性

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv"); 
alert(div.getAttribute("id")); //"myDiv" 
alert(div.getAttribute("class")); //"bd" 
alert(div.getAttribute("title")); //"Body text" 
alert(div.getAttribute("lang")); //"en" 
alert(div.getAttribute("dir")); //"ltr"

setAttribute

设置特性这个方法接受两个参数:要设置的特性名和值。
div.setAttribute("id", "someOtherId"); 
div.setAttribute("class", "ft"); 
div.setAttribute("title", "Some other text"); 
div.setAttribute("lang","fr"); 
div.setAttribute("dir", "rtl");

removeAttribute

这个方法用于彻底删除元素的特性。
div.removeAttribute("class");

attributes 属性 

attributes 的方法不够方便,因此开发人员更多的会使用,getAttribute()、removeAttribute() setAttribute() 方法。
Element 类型是使用 attributes 属性的唯一一个 DOM 节点类型。
  • getNamedItem(name):返回 nodeName 属性等于 name 的节点;
  • removeNamedItem(name):从列表中移除 nodeName 属性等于 name 的节点;
  • setNamedItem(node):向列表中添加节点,以节点的 nodeName 属性为索引;
  • item(pos):返回位于数字 pos 位置处的节点。
getNamedItem
var id = element.attributes.getNamedItem("id").nodeValue;

// 以下是使用方括号语法通过特性名称访问节点的简写方式。
var id = element.attributes["id"].nodeValue;
nodeValue 设置为新值
element.attributes["id"].nodeValue = "someOtherId";
removeNamedItem
removeNamedItem() 返回表示被删除特性的 Attr 节点。
var oldAttr = element.attributes.removeNamedItem("id");
setNamedItem

setNamedItem()是一个很不常用的方法,通过这个方法可以为元素添加一个新特性。

element.attributes.setNamedItem(newAttr);
创建元素

createElement

var div = document.createElement("div");

// 添加子节点
div.id = "myNewDiv"; 
div.className = "box";
要把新元素添加到文档树,可以使用 appendChild() insertBefore()或 replaceChild() 方法。

appendChild

// 会把新创建的元素添加到文档的<body>元素中
document.body.appendChild(div);
元素的子节点
<ul id="myList"> 
 <li>Item 1</li> 
 <li>Item 2</li> 
 <li>Item 3</li> 
</ul>

var ul = document.getElementById("myList"); 
var items = ul.getElementsByTagName("li");


for (var i=0, len=items.length; i < len; i++){ 
 if (items[i].nodeType == 1){ 
 //执行某些操作
 } 
}

DOM 操作技术

动态脚本

function loadScript(url){ 
 var script = document.createElement("script"); 
 script.type = "text/javascript"; 
 script.src = url; 
 document.body.appendChild(script); 
}


// 过调用这个函数来加载外部的 JavaScript 文件
loadScript("client.js");
问题只有一个:怎么知道脚本加载完成呢?遗憾的是,并没有什么标准方式来探知这一点。
另一种指定 JavaScript 代码的方式是行内方式,
function loadScriptString(code){ 
 var script = document.createElement("script"); 
 script.type = "text/javascript"; 
 try { 
 script.appendChild(document.createTextNode(code)); 
 } catch (ex){ 
 script.text = code; 
 } 
 document.body.appendChild(script); 
}

// 调用这个函数
loadScriptString("function sayHi(){alert('hi');}");

动态样式

需要注意的是,必须将 <link> 元素添加到 <head>而不是<body> 元素,才能保证在所有浏览器中的行为一致。
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); 
}

//调用 loadStyles()函数
loadStyles("styles.css");
另一种定义样式的方式是使用 <style> 元素来包含嵌入式 CSS
function loadStyleString(css){ 
 var style = document.createElement("style");
 style.type = "text/css"; 
 try{ 
 style.appendChild(document.createTextNode(css)); 
 } catch (ex){ 
 style.styleSheet.cssText = css; 
 } 
 var head = document.getElementsByTagName("head")[0]; 
 head.appendChild(style); 
}

loadStyleString("body{background-color:red}");

操作表格 

详见300

//创建 table 
var table = document.createElement("table"); 
table.border = 1; 
table.width = "100%"; 
//创建 tbody 
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[1].appendChild(document.createTextNode("Cell 2,1")); 
//创建第二行
tbody.insertRow(1); 
tbody.rows[1].insertCell(0); 
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2")); 
tbody.rows[1].insertCell(1); 
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2")); 
//将表格添加到文档主体中
document.body.appendChild(table);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值