您的位置:首页 > Web前端 > HTML

使用DOM操纵HTML文档几点粗浅认识

2013-12-02 16:24 218 查看

1 何为DOM?

www.w3.org官方的解释是:DOM 是HTML和XML文档的编程API。它定义了文档的逻辑结构和访问、操纵文档的方法。利用这组API,程序员可以对XML或HTML文档中的元素和内容进行任意的增删改查。这组API是与平台无关的,而且也是与编程语言无关的。这里的“对象模型”的含义取之于面向对象的编程思想。既然是对象就不仅包含属性,也包含操作属性的方法。所以DOM标准定义了这些属性和方法的名字及含义。

DOM包括DOM Core和DOM HTML两部分,DOM Core是用来操作XML的部分,也是操作HTML的基础。DOM HTML则是在DOM Core的基础上专门针对HTML增加了更多的对象、属性、方法。

2 DOM中节点(Node)的类型与继承

DOM把文档视为由Node构成的层级结构。构成文档的Node有很多类型,DOM Core 1中就定义了12中节点类型,不同节点类型具有不同的属性和方法。这12中节点类型如下:

Document--文档节点,代表整个文档,可以有子节点,只能有一个元素类型的子节点。
DocumentFragment
DocumentType--不能有子节点
EntityReference
Element--元素节点,这也许是最重要的节点类型了吧
Attr-属性节点
ProcessingInstruction
Comment
Text--文本节点,不能有子节点
CDATASection
Entity
Notation

DOM定义了上述每种节点类型的属性和方法,这里把Node这祖先类型和具体的Element类型、以及Attr类型的定义摘抄如下:

interface Node {
  // NodeType
  const unsigned short      ELEMENT_NODE       = 1;
  const unsigned short      ATTRIBUTE_NODE     = 2;
  const unsigned short      TEXT_NODE          = 3;
  const unsigned short      CDATA_SECTION_NODE = 4;
  const unsigned short      ENTITY_REFERENCE_NODE = 5;
  const unsigned short      ENTITY_NODE        = 6;
  const unsigned short      PROCESSING_INSTRUCTION_NODE = 7;
  const unsigned short      COMMENT_NODE       = 8;
  const unsigned short      DOCUMENT_NODE      = 9;
  const unsigned short      DOCUMENT_TYPE_NODE = 10;
  const unsigned short      DOCUMENT_FRAGMENT_NODE = 11;
  const unsigned short      NOTATION_NODE      = 12;

  readonly attribute  DOMString            nodeName;
           attribute  DOMString            nodeValue;
                                                 // raises(DOMException) on setting
                                                 // raises(DOMException) on retrieval
  readonly attribute  unsigned short       nodeType;
  readonly attribute  Node                 parentNode;
  readonly attribute  NodeList             childNodes;
  readonly attribute  Node                 firstChild;
  readonly attribute  Node                 lastChild;
  readonly attribute  Node                 previousSibling;
  readonly attribute  Node                 nextSibling;
  readonly attribute  NamedNodeMap         attributes;
  readonly attribute  Document             ownerDocument;
  Node                      insertBefore(in Node newChild, 
                                         in Node refChild)
                                         raises(DOMException);
  Node                      replaceChild(in Node newChild, 
                                         in Node oldChild)
                                         raises(DOMException);
  Node                      removeChild(in Node oldChild)
                                        raises(DOMException);
  Node                      appendChild(in Node newChild)
                                        raises(DOMException);
  boolean                   hasChildNodes();
  Node                      cloneNode(in boolean deep);
};


interface Element : Node {
  readonly attribute  DOMString            tagName;
  DOMString                 getAttribute(in DOMString name);
  void                      setAttribute(in DOMString name, 
                                         in DOMString value)
                                         raises(DOMException);
  void                      removeAttribute(in DOMString name)
                                            raises(DOMException);
  Attr                      getAttributeNode(in DOMString name);
  Attr                      setAttributeNode(in Attr newAttr)
                                             raises(DOMException);
  Attr                      removeAttributeNode(in Attr oldAttr)
                                                raises(DOMException);
  NodeList                  getElementsByTagName(in DOMString name);
  void                      normalize();
};


interface Attr : Node {
  readonly attribute  DOMString            name;
  readonly attribute  boolean              specified;
           attribute  DOMString            value;
};


对于DOM HTML部分,则针对HTML对一些类进行了进一步的扩展,如对Element进行扩展,产生了HTMLElement类,如下:
interface HTMLElement : Element {
           attribute  DOMString            id;
           attribute  DOMString            title;
           attribute  DOMString            lang;
           attribute  DOMString            dir;
           attribute  DOMString            className;
};
HTML中的各种具体的元素节点也都从HTMLElement进行了进一步的扩展,如超链接标签<a >对应的HTMLAnchorElement:
interface HTMLAnchorElement : HTMLElement {
           attribute  DOMString            accessKey;
           attribute  DOMString            charset;
           attribute  DOMString            coords;
           attribute  DOMString            href;
           attribute  DOMString            hreflang;
           attribute  DOMString            name;
           attribute  DOMString            rel;
           attribute  DOMString            rev;
           attribute  DOMString            shape;
           attribute  long                 tabIndex;
           attribute  DOMString            target;
           attribute  DOMString            type;
  void                      blur();
  void                      focus();
};


3 令人费解的文本节点---html文档中节点类型实例分析

为了弄清楚HTML文档中的节点关系,我们针对一段html代码进行分析。

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
	<div id="container">
		我是文本节点
		<p class="myclass">我也是文本类型的节点</p>
		<br />
	</div>
	<script>
		var div = document.getElementById('container');
		var childs = div.childNodes;
		for(i=0; i<childs.length; i++)
		{
			document.write(i + "--节点类型:" + childs[i].nodeType + 
				", 节点名称:" + childs[i].nodeName + 
				", 节点值:" + childs[i].nodeValue  + 
				", 节点值长度:" + (childs[i].nodeValue==null?"0":childs[i].nodeValue.length)
				+"<br/>");
		}
	</script>
</body>
</html>


这段js代码的输出结果如下:

0--节点类型:3, 节点名称:#text, 节点值: 我是文本节点 , 节点值长度:12

1--节点类型:1, 节点名称:P, 节点值:null, 节点值长度:0

2--节点类型:3, 节点名称:#text, 节点值: , 节点值长度:3

3--节点类型:1, 节点名称:BR, 节点值:null, 节点值长度:0

4--节点类型:3, 节点名称:#text, 节点值: , 节点值长度:2

如果你能算出这段js代码的输出结果,那么说明对html文档结构与DOM的对应关系有比较细致的了解了,如果和你想的不一样,那么请继续看下面的分析:

首先是0号子节点:节点类型是文本节点,文本节点的节点值就是文本本身。对应“我是文本节点”这段文本,为什么长度是12而不是6呢?明明只有6个字啊。请注意这段文字前后的空格,其前面有2个Tab字符,下一行的前面也有两个Tab字符,这样就是6+2+2=10了,剩下的2个在哪里呢?那就是两个换行符。也就是说这个文本节点的内容其实是:\n\t\t我是文本节点\n\t\t,正好是12个字符。

再看1号节点,类型是元素节点,对应html中的<p></p>标签,元素标签的节点值都是null。
再看2号节点,是文本节点,基于0号节点的分析,我们知道这个文本的内容就是 \n\t\t,正好3个字符。
再看3号节点,是元素节点,对应<br/>标签,元素节点的节点值都是null。
再看4号节点,文本节点,对应 \n\t,正好2个字符。

由此可见,虽然html对于空白和换行直接忽略,但是使用DOM解析的时候,这些空白和换行会无故增加很多节点,增加浏览器端CPU和内存损耗。

4 DOM有时候会侵犯CSS地盘

css规定了html文档中元素如何呈现,注意这里是“元素”而不是节点。因为CSS选择器只能选择html中的元素节点,而不能选择其他类型的节点,如文本节点。那么为什么可以改变文本的显示呢?这是因为CSS是具有继承性的,文本节点必然是元素节点的子节点,所以它继承了元素节点的CSS特性。

由于选择器不能选择文本节点,所以处于同一元素节点下面的两个文本节点不能有各自独立的CSS特征值。如下:

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<style>
			body{color:red;}
		</style>
	</head>
	<body>
		我是第一段文本
		<br/>
		我是第二段文本
	</body>
</html>


这两个文本节点 ,都是body这个元素节点的子节点,我们无法通过css选择器分别为其指定显示特征。可见,元素节点是具有CSS特征的最小单位
除此之外,css的选择器有时候不能满足用户需求,如一个表格的奇数行和偶数行采用不同的背景色,虽然可以通过为每一行增加class属性来通过css完成,但是一旦表格行数巨大,这将导致非常烦躁。此时使用DOM来动态设置反而更加简单。也许未来CSS的选择器能力将更加灵活强大,那样就没有必要让DOM来干这些”不正当“业务了。

5 属性节点的两种读写方式间的差别

直接上一个测试代码,看看属性节点到底什么意思。
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
	<div id="container" style="color:red;"></div>
	<script>
		var div = document.getElementById('container');
		var childs = div.attributes;
		for(i=0; i<childs.length; i++)
		{
			document.write(i + "--节点类型:" + childs[i].nodeType + 
				", 节点名称:" + childs[i].nodeName + 
				", 节点值:" + childs[i].nodeValue  + 
				", 节点值长度:" + (childs[i].nodeValue==null?"0":childs[i].nodeValue.length)
				+"<br/>");
		}
	</script>
</body>
</html>


输出结果为:

0--节点类型:2, 节点名称:id, 节点值:container, 节点值长度:9

1--节点类型:2, 节点名称:style, 节点值:color:red;, 节点值长度:10

属性节点的类型代号为2,节点名称就是键名,节点的值为字符串。属性节点分为两种,一种是DOM Core和DOM HTML中规定的,如HTMLElement类规定了id,title等属性;另一种是程序员自定义的属性,键名可以是任意字符串。对于自定义的属性,只能通过getAttribute()来读取,通过setAttribute()来设置。而对于第一种属性来说,除了通过getAttribute()和setAttribute()来读写外,还可以直接使用点语法来读写,如下所示:

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
	<input type="text" id="name" title="张三" />
	<script>
		var input = document.getElementById('name');

		input.title='李四';
		//input.setAttribute('title', '李四');
		var name1 = input.getAttribute('title');
		var name2 = input.title;
		document.write(name1 + "," + name2);
	</script>
</body>
</html>


上面两种方式,都可以改变input的title属性,效果完全相同。但是这并不是说对所有的属性都适合这条规则。同样的代码,我们把title属性替换成value属性。

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
	<input type="text" id="name" value="张三" />
	<script>
		var input = document.getElementById('name');

		//input.value='李四';
		input.setAttribute('value', '李四');
		var name1 = input.getAttribute('value');
		var name2 = input.value;
		document.write(name1 + "," + name2);
	</script>
</body>
</html>


运行结果表明,如果通过点语法改变value的值,则通过getAttribute('value')获得的值并没有因此改变;而如果通过setAttribute改变value的值,则通过点语法读取的value值会随之改变。可见此处的value属性有些特殊。

再看如下代码:

<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<script>
		function f()
		{
			var input = document.getElementById('name');
			var name1 = input.getAttribute('value');
			var name2 = input.value;
			alert(name1 + "," + name2);
		}
	</script>
</head>
<body>
	<input type="text" id="name" value="张三" />
	<input type="button" id="button" value="测试" onclick="f();" />

</body>
</html>


页面显示后,我们在浏览器中手动修改输入框表单中的值为”李四“,如下所示:



可见,虽然我们修改了控件的值,但是通过getAttribute('value')读取到的属性值并没有改变,而通过input.value读取的值却实时反映了这种变化。其实这个例子中我们手动修改文本框的值和通过input.value="李四”的效果一样,都是没有改变名为value的属性值。

我们来看看HTMLInputElement的定义:
interface HTMLInputElement : HTMLElement {
           attribute  DOMString            defaultValue;
           attribute  boolean              defaultChecked;
  readonly attribute  HTMLFormElement      form;
           attribute  DOMString            accept;
           attribute  DOMString            accessKey;
           attribute  DOMString            align;
           attribute  DOMString            alt;
           attribute  boolean              checked;
           attribute  boolean              disabled;
           attribute  long                 maxLength;
           attribute  DOMString            name;
           attribute  boolean              readOnly;
           attribute  DOMString            size;
           attribute  DOMString            src;
           attribute  long                 tabIndex;
  readonly attribute  DOMString            type;
           attribute  DOMString            useMap;
           attribute  DOMString            value; //这就是那个特殊的value属性
  void                      blur();
  void                      focus();
  void                      select();
  void                      click();
};



哪位高人能解释为什么这个名为value的属性会出现这种特殊的情况?????



与此相关的问题是,DOM也不能通过style属性读取元素来自外部css文件的配置信息。

另外,html标签中的属性名字和DOM定义属性名字也有不一致的时候,如HTML中的class属性,由于是js的保留字,所以不能直接使用,DOM将其对应到了className这个名字。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: