查找元素
var div = document.getElementById("myDiv");//取得<div>元素的引用
IE8及较低版本不区分ID的大小写,因此“myDiv”和“mydiv”会被当做相同的元素ID。
如果页面中多个元素的ID值相同,getElementById()只返回文档中第一次出现的元素。IE7及较低版本还为此方法添加了一个有意思的“怪癖”:name特性与给定ID匹配的表单元素也会被该方法返回。如果有哪个表单元素的name特性等于指定的ID,而且该元素在文档中位于带有给定ID的元素前面,那么IE就会返回那个表单元素:
<input type="text" name="myElement" value="Text field">
<div id="myElement">A div</div>
document.getElementById("myElement ");
以上代码在IE7中结果会返回<input>元素;而在其他所有浏览器中,都会返回对<div>元素的引用。为了避免IE中存在的这个问题,最好的办法是不让表单字段的name特性与其他元素的ID相同。
var images = document.getElementsByTagName("img");
alert(images.length); //输出图表的数量
alert(images[0].src); //输出第一个图像元素的src 特性
aler t(images.item(0).src); //输出第一个图像元素的src 特性
HTMLCollection对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的name特性取得集合中的项:
<img src="myimage.gif" name="myImage">
var myImage = images.namedItem("myImage");
var myImage = images["myImage"];
var radios = document.getElementsByName("color");
特殊集合
除了属性和方法,document对象还有一些特殊的集合。这些集合都是HTMLCollection对象,为访问文档常用的部分提供了快捷方式:
- document.anchors,包含文档中所有带name特性的<a>元素;
- document.applets,包含文档中所有的<applet>元素,因为不再推荐使用<applet>元素,所以这个集合已经不建议使用了。
- document.forms,包含文档中所有的<form>元素,与document.getElementsByTagName("form")得到的结果相同。
- document.images,包含文档中所有的<img>元素,与document.getElementsByTagName("img")得到的结果相同。
- document.links,包含文档中所有带href特性的<a>元素。
这个特殊集合始终都可以通过HTMLDocument对象访问到,而且,与HTMLcollection对象类似,集合中的项也会随着当前文档内容的更新而更新。
文档写入
有一个document对象的功能已经存在很多年了,那就是将输出流写入到网页中的能力。这个能力体现在下列4个方法中:write()、writeln()、open()、 close()。
Element类型
除了Document类型之外,Element类型就要算是Web编程中最常用的类型了。Element类型用于表现XML或HTML元素,提供了对元素标签名、子节点及特性的访问。
var div = document.getElementById("myDiv");
alert(div.tagName); //"DIV"
alert(div.tagName == div.nodeName); //true
这里的元素标签名是div,它拥有一个值为“myDiv”的ID。可是,div.tagName实际上输出的是“DIV”而非“div”。在HTML中,标签名始终都以全部大写表示;而在XML(有时候也包括XHTML)中,标签名则始终会与源代码中的保持一致。假如你不确定自己的脚本将会在HTML还是XML文档中执行,最好是在比较之前将标签转换为相同的大小写形式:
if (element.tagName == "div"){ //不能这样比较,很容易出错!
//在此执行某些操作
}
if (element.tagName.toLowerCase() == "div"){ //这样最好
//在此执行某些操作
}
HTML元素
所有HTML元素都由HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
var div = document.getElementById("myDiv");
alert(div.id); //"myDiv""
alert(div.className); //"bd"
alert(div.title); //"Body text"
alert(div.lang); //"en"
alert(div.dir); //"ltr"
像下面这样通过为每个属性赋予新的值,也可以修改对应的每个特性:
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";
取得特性
操作特性的DOM方法主要有三个,分别是getAttribute()、setAttribute()、 removeAttribute()。
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"
在IE7及以前版本中,通过getAttribute()方法访问style特性或onclick这样的事件处理特性时,返回的值与属性的值相同。换句话说,getAttribute("style")返回一个对象,而getAttribute("obclick")返回一个函数。虽然IE8已经修复了这个bug,但不同IE版本间的不一致性,也是导致开发人员不使用getAttribute()访问HTML特性的一个原因。
在IE7及以前版本中,setAttribute()存在一些异常行为。通过这个方法设置class和style特性,没有任何效果,而使用这个方法设置事件处理程序特性时也一样。尽管到了IE8才解决这些问题,但我们还是推荐通过属性来设置特性。
IE6及以前版本不支持removeAttribute()。
attribute属性
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个“动态”的集合。
- getNamedItem(name):返回nodeName属性等于name的节点
- removeNamedItem(name):从列表中移除nodeName属性等于name的节点
- setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引
- item(pos):返回位于数字pos位置处的节点。
attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是特性的值。要取得元素的id特性,可以使用一下代码:
var id = element.attributes.getNamedItem("id").nodeValue;
var id = element.attributes["id"].nodeValue;
创建元素
var div = document.createElement("div");
元素的子节点
for (var i=0, len=element.childNodes.length; i < len; i++){
if (element.childNodes[i].nodeType == 1){
//执行某些操作
}
}
Text类型
分割文本节点
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);
var newNode = element.firstChild.splitText(5);
alert(element.firstChild.nodeValue); //"Hello"
alert(newNode.nodeValue); //" world!"
alert(element.childNodes.length); //2
Comment类型
注释在DOM中是通过Comment类型来表示的。
<div id="myDiv"><!--A comment --></div>
var div = document.getElementById("myDiv");
var comment = div.firstChild;
alert(comment.data); //"A comment"
另外,使用document.createComment()并为其传递注释文本也可以创建注释节点:
var comment = document.createComment("A comment ");
开发人员很少会创建和访问注释节点,因为注释节点对算法鲜有影响。此外,浏览器也不会识别位于</html>标签后面的注释。如果要访问注释节点,一定要保证它们是<html>元素的后代(即位于<html>和</html>之间)。
DOM操作技术
动态脚本
<script type="text/javascript" src="client.js"></script>
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "client.js";
document.body.appendChild(script);
尝试标准的DOM文本节点方法,因为除了IE(在IE中会导致抛出错误),所有浏览器都支持这种方式。如果这行代码抛出了错误(IE将<script>视为一个特殊的元素,不允许DOM访问其子节点),那么说明是IE,于是就必须使用text属性了。整个过程可以用以下函数来表示:
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');}");
这种方式加载的代码会在全局作用域中执行,而且当脚本执行后将立即可用。实际上,这样执行代码与在全局作用域中把相同的字符串传递给eval()是一样的。
动态样式
<link rel="stylesheet" type="text/css" href="styles.css">
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "style.css";
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);
与动态添加嵌入式脚本类似:
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}");
如果专门针对IE编写代码,务必小心使用styleSheet.cssText属性。在重用同一个<style>元素并在此设置这个属性时,有可能会导致浏览器崩溃。同样,将cssText属性设置为空字符串也可能导致浏览器崩溃。
使用NodeList
下面代码会出现死循环:
var divs = document.getElementsByTagName("div"),
i,
div;
for (i = 0; i < divs.length; i++) {
div = document.createElement("div");
document.body.appendChild(div);
}
改为:
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);
}
这个例子中初始化了第二个变量len。由于len中保存着对divs.length在循环开始时的一个快照,因此就会避免上一个例子中出现的无限循环问题。
一般来说,应该尽量减少访问NodeList的次数。因为每次访问NodeList,都会运行一次基于文档的查询。所以,可以考虑将从NodeList中取得的值缓存起来。