JavaScript笔记:DOM

本文详细介绍了DOM对象模型的基础知识,包括节点类型、文档结构、元素特性等,并提供了多种实用的DOM操作技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DOM对象是针对HTML和XML文档的一个API。DOM描绘了一个层次化的节点树,允许开发人员添加修改页面的一部分。

节点层次

DOM可以将HTML和XML文档描绘成一个多层节点组成的结构。节点分为不同的类型,每种类型分别表示文档中不同的信息或标记。每个节点都拥有各自的特点、数据和方法,与其它节点也可能存在某种关系。节点之间的关系构成了层次,所有页面的标记则表现为以特定节点为跟节点的树形结构。

1、Node类型

DOM1级定义了一个Node接口,该接口将由DOM中的所有节点类型实现。这个Node接口在JS中是作为Node类型实现的,除了IE浏览器之外,其他所有的浏览器都可以访问到这个类型。JS中所有的节点都继承自Node类型,因此所有的节点都共享着相同的基本属性和方法。

每个节点都有一个nodeType属性,用于表示节点的类型。节点类型由在Node类型中定义的下列12个数值常量表示,任何节点类型必居其一。

Node.ELEMENT_NODE(1) 
Node.ATTRIBUTE_NODE(2) 
Node.TEXT_NODE(3) 
Node.CDATA_SECTION_NODE(4) 
Node.ENTITY_REFERENCE_NODE(5) 
Node.ENTITY_NODE(6) 
Node.PROCESSING_INSTRUCTION_NODE(7)    
Node.COMMENT_NODE(8)
Node.DOCUMENT_NODE(9) 
Node.DOCUMENT_TYPE_NODE(10) 
Node.DOCUMENT_FRAGMENT_NODE(11)    
Node.NOTATION_NODE(12)。

通过比较上面这些常量,可以很容易的确定节点的类型。

if (someNode.nodeType == 1){  //在所有浏览器中有效 
    alert("Node is an element.");
}

nodeName和nodeValue属性:

要了解节点的具体信息,可以使用nodeName和nodeValue属性。这两个属性完全取决于节点的类型。
检查节点类型:

if (document.getElementById("first").nodeType == 1){
    value = document.getElementById("first").nodeName; 
    console.log(value);// DIV
}

对于元素节点,nodeName保存的是元素的标签名,而nodeValue始终为null。
节点关系:

文档中的所有节点都存在这样或那样的关系。节点之间的关系就好像家族的家谱树。例如,在HTML中body是html元素的子元素,反过来说,html元素就是body元素的父元素。而head元素,则可以看成是body元素的同胞元素。每个节点都有一个childNodes属性,保存着一个NodeList对象,注意,NodeList对象并不是Array的实例,它是对DOM动态查询的结果。
使用方法:

var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;

使用下列方法可以将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列表中的所有节点都有相同的父节点。另外,包含在childNodes列表中的所有节点都是同胞节点,可以使用节点属性previousSibling/nextSibling来访问节点的同胞节点。

if (someNode.nextSibling === null){
    alert("Last node in the parent’s childNodes list.");
} else if (someNode.previousSibling === null){
    alert("First node in the parent’s childNodes list.");
}

父节点的firstChild和lastChild分别指向其子节点的第一个和最后一个节点。

另外hasChildNodes()也是一个好用的方法,当节点包含子节点的时候,该方法返回true。

所有节点都有的最后一个属性是ownerDocument,该属性指向表示整个文档的文档节点。

操作节点:

因为关系指针都是只读的,所以DOM提供了一些操作节点的方法。其中,最常用的就是appendChild()方法,用于向childNodes列表的末尾添加一个节点。更新完成后,appendChild()方法返回新增的节点。

var returnedNode = someNode.appendChild(newNode); 
alert(returnedNode == newNode); //true 
alert(someNode.lastChild == newNode); //true

如果传入appendChild函数的节点已经是文档的一部分了,那结果就是将传入的节点移动到指定的位置。
如果需要把指定节点放在childNodes列表的某个特定位置上,而不是最后一个,那么就需要用到insertBefore方法。这个方法接受两个参数:要插入的节点和作为参照的节点。如果参照节点是null,那么insertBefore方法就和appendChild方法的作用一样了。

//插入后成为最后一个节点         
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函数:该函数接受两个参数,要插入的节点和要替换的节点。要替换的节点将由这个函数返回,同时要插入的节点将在DOM中取代被替换掉的节点。


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

如果只想移除而不是替换节点的话,可以使用函数removeChild(),该函数接受一个参数:要移除的节点。

其它方法:

有两个方法是所有类型的节点都具有的:cloneNode(),normalize()。
cloneNode方法接受一个布尔值的参数,表示是否进行深复制。如果进行深复制,那么该节点的所有包括子节点在内的信息都会被复制。如果只是浅复制,那么将只复制节点自身。
例如:

<ul>
   <li>item 1</li>
   <li>item 2</li>
   <li>item 3</li>
</ul>

假设我们已经将ul节点保存在了myList中,那么下面这段代码就可以让我们看出两种复制的区别。

var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3      
var shallowList = myList.cloneNode(false);
alert(shallowList.childNodes.length); //0

normalize方法用来处理文档树中的文本节点。当在某个节点上调用这个方法时,就会在该节点的后代中查找:1)空文本节点,找到后删除;2)相邻的文本节点,找到后合并成一个。

2、Document类型:

JavaScript通过Document类型表示文档。在浏览器中,document是HTMLDocument的一个实例,表示整个HTML页面。而且document是window对象的一个属性,因此,可以将其作为全局属性来访问。
document节点具有下列属性:
nodeType值为9;
nodeName的值为#document;
nodeValue、parentNode、ownerDocument的值为null。

Document类型可以表示HTML页面,不过最常见的应用还是作为document对象。通过这个对象,不仅可以获得与页面有关的信息,而且还可以操作页面的外观极其底层结构。

1、文档的子节点

document的documentElement属性:该属性始终指向HTML页面中的html元素。另外,document的childNodes也可以用来访问页面的元素。

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

作为HTMLDocument的实例,document还有一个属性body,直接指向页面的body标签:

var body = document.body; //取得对<body>的引用

所有的浏览器都支持document.documentElemen和document.body属性。

2、文档信息

作为HTMLDocument文档的一个实例,document还有一些普通Document对象所没有的属性,这些属性提供了document对象所表现的一些网页信息。
例如,title:

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

接下来的三个属性都和网页请求有关:

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

这三个属性中,只有domain是可以设置的。但是,出于安全性问题的考虑,只能设置成URL中包含的域,否则就会出错:

//假设页面来自于p2p.wrox.com域
document.domain = "wrox.com"; //成功
document.domain = "nczonline.net"; //出错

浏览器对domain属性还有一个限制,即如果域名一开始是松散的,就不能将其设置为紧绷的。

//假设页面来自于p2p.wrox.com域
document.domain = "wrox.com"; //松散的,成功
document.domain = "p2p.wrox.com"; //紧绷的,失败

3、查找元素

最常见的DOM应用,应该就是获得特定的某个或某组元素的引用,然后再执行一些操作了。取得元素的操作可以使用document对象的几个方法完成:例如,getElementById()、getElementsByTagName()、getElementsByTagName()。

例子:

var images = document.getElementsByTagName("img");
alert(images.length);
alert(images[0].src);
alert(images.item(0).src);

4、特殊集合

document.anchors:文档中所有带name属性的a标签
document.forms:文档中所有form标签
document.images:文档中所有img标签
document.links:文档中所有带href属性的a标签

5、DOM一致性检测

由于DOM包含了多个级别,也包含了多个部分,因此,检测浏览器实现了DOM的那些部分就十分重要了。
使用方法:

var hasXmlDom = document.implementation.hasFeature("XML", "1.0");

如果浏览器支持该版本,将返回true。

6、文档写入

将输出流写入网页的能力:

<html>
    <head>
        <title>document.write() Example</title>
    </head>
    <body>
        <p>The current date and time is:
            <script type="text/javascript">
                document.write("<strong>" + (new Date()).toString() + "</strong>"); 
            </script>
        </p>
    </body>
</html>

此外,还可以使用此方法动态的包含外部资源:


<html>
    <head>
        <title>document.write() Example 3</title>
    </head>
    <body>
        <script type="text/javascript">
            document.write("<script type=\"text/javascript\" src=\"file.js\">" +"<\/script>");
        </script>
    </body>
</html>

请注意双引号中的转意字符的写法。

Element类型

Element节点具有以下特点:
nodeType为1
nodeName/tagName为节点的标签名
nodeValue为null
parentNode可能是Document或Element

1、HTML元素

<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);//"ltd" 语言的方向

// 修改属性
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";

2、取得特性

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"

用这个方法可以获得标签自定义的属性:

<div id="myDiv" data-my_special_attribute="hello!"></div>

var value = div.getAttribute("my_special_attribute");

另外,有两个特殊的属性,它们通过getAttribute访问或者通过获取元素属性的值得到的结果不同:
第一个是style:当通过getAttribute方法访问时,返回的是字符串,而通过元素的属性的方法访问时,返回的则是一个对象。
第二个是onclick这样的事件处理程序:通过getAttribute访问时,返回的是JS代码的字符串,而通过元素属性访问,返回的则是这个函数。

3、设置特性

div.setAttribute("id", "someOtherId");
div.setAttribute("class", "ft");
div.setAttribute("title", "Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir", "rtl");

4、attributes属性

Element类型是使用attributes属性的唯一的一个DOM节点类型。attributes属性中包含一个NamedNodeMap,是一个动态的集合。NamedNodeMap对象拥有下列方法:
getNamedItem(name):返回nodeName属性等于name的节点。
removeNamedItem(name):从列表中移除nodeName属性等于name的节点。setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引。
item(pos):返回位于pos位置的节点。

<div id="first" class="mydiv"> 萝卜 </div>
// method 1
var element = document.getElementById("first");
var id = element.attributes.getNamedItem("class").nodeValue;
console.log(id);// media
// method 2
var element = document.getElementsByTagName("div");
var id = element[0].attributes.getNamedItem("class").nodeValue;
console.log(id);// mydiv

5、创建元素

var div = document.createElement("div");
// 为元素添加属性
div.id = "myNewDiv";
div.className = "box";
// 将元素加入DOM树
document.body.appendChild(div);
// IE下:
var div = document.createElement("<div id=\"myNewDiv\" class=\"box\"></div >");

6、元素的子节点

<ul id="myList">
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
</ul>

var element = document.getElementById("myList");
var num = element.childNodes.length;
console.log(num);// 7

在不是IE的浏览器下,上面这个ul包含了7个字节点:包括三个li节点和4个文本节点。正确的访问li的方法为:

for (var i=0, len=element.childNodes.length; i < len; i++){
    if (element.childNodes[i].nodeType == 1){
        //执行某些操作
    }
}
// or
var ul = document.getElementById("myList");
var items = ul.getElementsByTagName("li");

Text类型

文本节点由Text类型表示,包含的是纯文本内容。text节点具有一下的特征:
nodeType为3
nodeName为#text
nodeValue为节点包含的文本
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将文本分为两个
substringData(offset, count) 提取从offset到offset+count的文本

<!--没有文本节点--> 
<div></div>
<!--有1个文本节点--> 
<div> </div>
<!--有1个文本节点--> 
<div>Hello World!</div>
var textNode = div.firstChild; //  div.childNodes[0]
div.firstChild.nodeValue = "Some other message";

修改文本节点时需要注意,此时的字符串要经过html编码,也就是说,有些符号会被转译:

//输出结果是"Some &lt;strong&gt;other&lt;/strong&gt; message" div.firstChild.nodeValue = "Some <strong>other</strong> message";

创建文本节点:

var textNode = document.createTextNode("<strong>Hello</strong> world!");
// 创建文本节点并加入DOM中。
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);

规范化文本节点

该方法可以将多个文本节点合并:

var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
var anotherTextNode = document.createTextNode("Yippee!");
element.appendChild(anotherTextNode);
document.body.appendChild(element);
alert(element.childNodes.length);    //2
element.normalize();
alert(element.childNodes.length);    //1
alert(element.firstChild.nodeValue); // "Hello world!Yippee!"

分割文本节点

该方法的作用和normalize刚好相反,是将一个文本节点分割为多个:

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类型

注释用Comment类型表示。特点:
nodeType值为8
nodeName为#comment
nodeValue为注释的内容
parentNode为Document或者Element
没有子节点

<div id="myDiv"><!--A comment --></div>
// 访问
var div = document.getElementById("myDiv");
var comment = div.firstChild;
alert(comment.data);    //"A comment"
// 创建
var comment = document.createComment("A comment ");

DocumentType类型

仅有Firefox Safari Opera支持。该类型包含了与文档doctype有关的信息。
nodeType:10
nodeName:doctype的名称
nodeValue:null
parentNode:Document
没有子节点

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">

alert(document.doctype.name);        //"HTML"

Attr类型

元素的特性在DOM中用Attr类型来表示。

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操作技术

动态脚本

使用script元素可以向页面中插入JavaScript代码,一种方式是通过src链接外部文件,另一种方式就是在标签内直接写入代码。
动态脚本,指的是,页面加载时不存在,但是在将来的某一个时刻,通过修改DOM动态添加的脚本。动态创建脚本有两个方式:

1、动态加载外部的JS文件

<script type="text/javascript" src="client.js"></script>

创建这个节点的DOM代码如下所示:

var script = document.createElement("script");
script.type = "text/javascript";
script.src = "client.js";
document.body.appendChild(script);

// 函数封装
function loadScript(url){
   var script = document.createElement("script");
   script.type = "text/javascript";
   script.src = url;
   document.body.appendChild(script);
}
// 加载文件
loadScript("client.js");

2、行内方式

<script type="text/javascript">
        function sayHi(){
            alert("hi");
        }
</script>

// DOM代码 不兼容IE
var script = document.createElement("script");
script.type = "text/javascript"; 
script.appendChild(document.createTextNode("function sayHi(){alert('hi');}")); 
document.body.appendChild(script);

// 兼容IE
var script = document.createElement("script"); 
script.type = "text/javascript";
script.text = "function sayHi(){alert('hi');}"; 
document.body.appendChild(script);

// 函数封装
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 rel="stylesheet" type="text/css" href="styles.css">
// DOM代码
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 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("styles.css");

// 第二种方式
<style type="text/css">
    body {
        background-color: red;
    }
</style>

// DOM方式
var style = document.createElement("style"); 
style.type = "text/css";
try{
    style.appendChild(document.createTextNode("body{background-color:red}"));
} catch (ex){
    style.styleSheet.cssText = "body{background-color:red}";
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);

// 函数封装
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}");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值