JS高程:读书摘要(七)DOM

2019-01-02  本文已影响0人  Upcccz

一、 DOM

DOM(文档对象模型)是针对HTMLXML文档的一个API(应用程序编程接口)。DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。DOM脱胎于Netscape 及微软公司创始的DHTML(动态HTML),但现在它已经成为表现和操作页面标记的真正的跨平台、语言中立的方式。

Node类型

DOM1级定义了一个Node接口,该接口将由DOM 中的所有节点类型实现。每个节点都有一个nodeType属性,用于表明节点的类型。节点类型由在Node 类型中定义的下列12 个数值常量来表示,任何节点类型必居其一:

节点类型nodeType
节点的nodeName和nodeValue

每个节点都有一个childNodes属性,其中保存着一个NodeList 对象。NodeList是一种类数组对象(可以通过下标访问,有length属性,但它并不是Array的实例),用于保存一组有序的节点,可以通过位置(索引 / 方括号写法)来访问这些节点。NodeList对象的独特之处在于,它实际上是基于DOM 结构动态执行查询的结果,因此DOM结构的变化能够自动反映在NodeList 对象中。我们常说,NodeList是有生命、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。

如何访问保存在NodeList中的节点——可以通过方括号,也可以使用item()方法。

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

使用Array.prototype.slice()方法可以将NodeList对象转换为数。

var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
var deepList = myList.cloneNode(true);
alert(deepList.childNodes.length); //3(IE < 9)或7(其他浏览器)

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

cloneNode()方法不会复制添加到DOM节点中的JavaScript属性,例如事件处理程序等。这个方法只复制特性、(在明确指定的情况下也复制)子节点,其他一切都不会复制。IE 在此存在一个bug,即它会复制事件处理程序,所以我们建议在复制之前最好先移除事件处理程序。

二、 Document 类型

document对象:在浏览器中,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document 对象是window 对象的一个属性,因此可以将其作为全局对象来访问。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。Document 节点具有下列特征: nodeType 的值为9;nodeName 的值为"#document"nodeValue 的值为nullparentNode 的值为null

document对象的属性

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

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

var doctype = document.doctype; //取得对<DOCTYPE>文档类型的引用

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

//取得完整的URL,不可设置
var url = document.URL;

//取得域名,可设置,但是不能随便设置
var domain = document.domain;

//假设页面来自p2p.wrox.com 域
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出错! 只能省略域名

//取得来源页面的URL,不可设置
var referrer = document.referrer;
查找元素

1、getElementById():接收一个参数:要取得的元素的ID。如果找到相应的元素则返回该元素,如果不存在带有相应ID 的元素,则返回null。注意,这里的ID必须与页面中元素的id特性(attribute)严格匹配,包括大小写。IE8 及较低版本不区分ID的大小写,因此"myDiv""mydiv"会被当作相同的元素ID。如果页面中多个元素的ID 值相同,getElementById()只返回文档中第一次出现的元素。

<input type="text" name="myElement" value="Text field">
<div id="myElement">A div</div>

基于这段HTML 代码,在IE7 中调用document.getElementById("myElement "),结果会返回<input>元素;而在其他所有浏览器中,都会返回对<div>元素的引用。为了避免IE中存在的这个问题,最好的办法是不让表单(<input><textarea><button><select>)字段的name特性与其他元素的ID相同。

2、getElementsByTagName()。这个方法接受一个参数,即要取得元素的标签名,返回一个HTMLCollection对象,作为一个“动态”集合,返回所有的标签元素组成的类数组。HTMLCollection 对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的name特性取得集合中的项。例如,

<img src="myimage.gif" name="myImage">
 // 那么就可以通过如下方式从images几个中取得对应name的<img>元素:
var images = document.getElementsByTagName("img");
var myImage = images.namedItem("myImage");

// HTMLCollection 还支持按名称访问项
var myImage = images["myImage"];

在通过元素调用这个方法时,除了搜索起点是当前元素之外,其他方面都跟通过document调用这个方法相同,因此结果只会返回当前元素的后代。

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

3、getElementsByClassName():这个方法接受一个参数,即要取得元素的类名,返回一个HTMLCollection对象,作为一个“动态”集合,返回所有拥有该类的元素组成的类数组。

4、为访问文档常用的部分提供了快捷方式:

这些特殊集合始终都可以通过HTMLDocument 对象访问到,而且,与HTMLCollection 对象类似,集合中的项也会随着当前文档内容的更新而更新。

文档写入

write()writeln()方法都接受一个字符串参数,即要写入到输出流中的文本。write()会原样写入,而writeln()则会在字符串的末尾添加一个换行符(\n)。在页面被加载的过程中,可以使用这两个方法向页面中动态地加入内容。方法open()close()分别用于打开和关闭网页的输出流。

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

这样做会创建一个DOM元素,而且可以在将来访问该元素。通过write()writeln()输出的任何HTML代码都将如此处理。

还可以使用write()writeln()方法动态地包含外部资源,例如JavaScript文件等。在包含JavaScript文件时,必须注意不能像下面的例子那样直接包含字符串"</script>",因为这会导致该字符串被解释为脚本块的结束,它后面的代码将无法执行。应该使用转义符做处理"<\/script>"

window.onload事件处理程序,等到页面完全加载之后延迟执行函数。函数执行之后,调用document.write()会重写整个页面内容。

三、Element 类型

Element 类型用于表现XMLHTML元素,提供了对元素标签名、子节点及特性的访问。Element节点具有以下特征:nodeType 的值为1;nodeName 的值为元素的标签名;nodeValue 的值为null

要访问元素的标签名,可以使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值。

所有HTML元素都由HTMLElement 类型表示,不是直接通过这个类型,也是通过它的子类型来表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。

id,元素在文档中的唯一标识符
title,有关元素的附加说明信息
lang,元素内容的语言代码,很少使用。
dir,语言的方向,值有ltr从左到右  rtl从右到做 ,很少使用

className,与元素的class 特性对应,即为元素指定的CSS类。
没有将这个属性命名为class,是因为class 是ECMAScript 的保留字。
通过className访问而不是class,**但是在使用操作元素属性的方法时需要使用class,与html上的属性保持一致**

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
操作元素属性

每个元素都有一或多个特性,这些特性的用途是给出相应元素或其内容的附加信息。操作特性的DOM方法主要有三个,分别是getAttribute()setAttribute()removeAttribute(),接受特性字符串作为第一个参数。setAttribute()可以传第二个参数作为设置的新值。特性的名称是不区分大小写的,即"ID""id"代表的都是同一个特性。另外也要注意,根据HTML5规范,自定义特性应该加上data-前缀以便验证。

注意,传递给getAttribute()的特性名与实际的特性名相同。因此要想得到class特性值,应该传入"class"而不是"className",后者只有在通过对象属性访问特性时才用。如果给定名称的特性不存在,getAttribute()返回null

1、 getAttribute()获取属性

任何元素的所有特性,也都可以通过DOM元素本身的属性来访问。不过,只有公认的(非自定义的)特性才会以属性的形式添加到DOM对象中。自定义属性只能通过getAttribute()来获得

<div id="myDiv" align="left" my_special_attribute="hello!"></div>

alert(div.id); //"myDiv"
alert(div.my_special_attribute); //undefined(IE 除外)
alert(div.align); //"left"

有两类特殊的特性,它们虽然有对应的属性名,但属性的值与通过getAttribute()返回的值并不相同。

由于存在这些差别,在通过JavaScript 以编程方式操作DOM时,开发人员经常不使用getAttribute(),而是只使用对象的属性。只有在取得自定义特性值的情况下,才会使用getAttribute()方法。

2、 setAttribute()获取属性
setAttribute(),这个方法接受两个参数:要设置的特性名和值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。通过setAttribute()方法既可以操作HTML特性也可以操作自定义特性。通过这个方法设置的特性名会被统一转换为小写形式,即"ID"最终会变成"id"。如果是使用这样的方式oDiv.myColor="div1"的方式设置的自定义属性,用getAttribute()是访问不到的。

3、 removeAttribute()删除属性
接受一个参数,即删除的属性key,这个方法用于彻底删除元素的特性。调用这个方法不仅会清除特性的值,而且也会从元素中完全删除特性,oDiv.removeAttribute("class");

创建元素

1、 document.createElement() 创建元素
这个方法只接受一个参数,即要创建元素的标签名(IE中可以传入完整的HTML标签字符串)。这个标签名在HTML文档中不区分大小写,而在XML(包括XHTML)文档中,则是区分大小写的。

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

2、 增加元素

3、删除元素

4、改变元素

元素的子节点

元素的childNodes属性中包含了它的所有子节点,这些子节点有可能是元素、文本节点、注释或处理指令。IE会返回所有的子元素节点,而其他浏览器则返回所有的子节点,这意味着在执行某项操作以前,通常都要先检查一下nodeTpye属性

for (var i=0, len=element.childNodes.length; i < len; i++){
    if (element.childNodes[i].nodeType == 1){ 
        //对子元素节点执行某些操作
    }
}

四、Text 类型

每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在。(只有空格也算是有内容)

如果这个文本节点当前存在于文档树中,那么修改文本节点的结果就会立即得到反映。另外,在修改文本节点时还要注意,此时的字符串会经过HTML(或XML,取决于文档类型)编码。换句话说,小于号、大于号或引号都会像下面的例子一样被转义。

Some <strong>other</strong> message 
转义为
Some &lt;strong&gt;other&lt;/strong&gt; message
创建文本节点

document.createTextNode() : 创建新文本节点,这个方法接受一个参数——要插入节点中的文本。与设置已有文本节点的值一样,作为参数的文本也将按照HTMLXML 的格式进行编码。创建的文本节点,可以使用元素的appendChild()将其添加到元素中,在没有添加进DOM树之前,都是属于孤儿节点,文档碎片。

var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);

一般情况下,每个元素只有一个文本子节点。不过,在某些情况下也可能包含多个文本子节点,比如使用appendChild()方法多次添加文本节点,那么这两个节点中的文本就会连起来显示,中间不会有空格。如果在一个包含两个或多个文本节点的父元素上调用normalize()方法,则会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue 值拼接起来的值。

浏览器在解析文档时永远不会创建相邻的文本节点。这种情况只会作为执行DOM操作的结果出现。

五、DOM操作技术

DOM操作往往是JavaScript程序中开销最大的部分,而因访问NodeList导致的问题最多。NodeList对象都是“动态的”,这就意味着每次访问NodeList对象,都会运行一次查询。有鉴于此,最好的办法就是尽量减少DOM操作。

动态脚本

指的是在页面加载时不存在,但将来的某一时刻通过修改DOM动态添加的脚本。

1、插入外部文件

function loadScript(url){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url; 
    document.body.appendChild(script); // 在这行代码执行之前都不会下载文件
}

loadScript("client.js");  // 方法调用时 动态添加script标签 并执行脚本

2、行内方式,也是添加script标签 ,但是脚本代码吧不是从外部引入,而是本地添加进去。所有写在<script></script>标签对中的代码都算是script元素的文本节点

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

Safari 3.0 之前的版本不能正确地支持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');}");
动态样式

能够把CSS 样式包含到HTML 页面中的元素有两个。其中,<link>元素用于包含来自外部的文件,而<style>元素用于指定嵌入的样式。

同理。操作DOM代码添加标签可以很容易地动态创建出作用CSS样式的元素:

1、外部引入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("myStyle.css");

加载外部样式文件的过程是异步的,也就是加载样式与执行JavaScript代码的过程没有固定的次序。

2、内部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}");

事实上,IE 此时抛出的错误(将<script><style>视为一个特殊的元素,不允许DOM访问其子节点。)。解决IE 中这个问题的办法,就是访问元素的styleSheet属性,该属性又有一个cssText属性,可以接受CSS代码

操作表格

为了方便构建表格,HTML DOM还为<table><tbody><tr>元素添加了一些属性和方法。

<table>元素添加的属性和方法如下。

<tbody>元素添加的属性和方法如下。

<tr>元素添加的属性和方法如下。

使用NodeList

理解NodeList及其“近亲”NamedNodeMapHTMLCollection,是从整体上透彻理解DOM的关键所在。这三个集合都是“动态的”;换句话说,每当文档结构发生变化时,它们都会得到更新。因此,它们始终都会保存着最新、最准确的信息。从本质上说,所有NodeList对象都是在访问DOM文档时实时运行的查询。

如果想要迭代一个NodeList,最好是使用length属性初始化第二个变量,然后将迭代器与该变量进行比较。因为NodeList是动态的,NodeList.length也是动态的,如果在迭代的过程对这个NodeList有添加Node的操作,将会无限迭代下去,所以可以使用变量暂存length:var len = NodeList.length

一般来说,应该尽量减少访问NodeList 的次数。因为每次访问NodeList,都会运行一次基于文档的查询。所以,可以考虑将从NodeList中取得的值缓存起来。

上一篇下一篇

猜你喜欢

热点阅读