《JavaScript 闯关记》之 DOM(下)

Element 范例

除了 Document 范例以外,Element 范例就要算是 Web 编程中最常常运用的范例了。Element 范例用于表现 XML 或 HTML 元素,供应了对元素标署名、子节点及特性的接见。Element 节点具有以下特性:

  • nodeType 的值为1;

  • nodeName 的值为元素的标署名;

  • nodeValue 的值为 null

  • parentNode 多是 DocumentElement

  • 其子节点多是 ElementTextCommentProcessingInstructionCDATASectionEntityReference

要接见元素的标署名,可以运用 nodeName 属性,也可以运用 tagName 属性;这两个属性会返回雷同的值(运用后者主要是为了清楚起见)。以下面的元素为例:

<div id="myDiv"></div>

可以像下面如许获得这个元素及其标署名:

var div = document.getElementById("myDiv");
console.log(div.tagName); // "DIV"
console.log(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 并增加了一些属性。增加的这些属性离别对应于每一个 HTML 元素中都存在的以下范例特性。

  • id,元素在文档中的唯一标识符。

  • title,有关元素的附加申明信息,平常经由历程东西提示条显现出来。

  • lang,元素内容的言语代码,很少运用。

  • dir,言语的方向,值为 "ltr"(left-to-right,从左至右)或 "rtl"(right-to-left,从右至左),也很少运用。

  • className,与元素的 class 特性对应,即为元素指定的 CSS 类。没有将这个属性命名为 class,是因为 class 是 JavaScript 的保留字。

上述这些属性都可以用来获得或修正响应的特性值。以下面的HTML元素为例:

<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>

元素中指定的一切信息,都可以经由历程以下 JavaScript 代码获得:

var div = document.getElementById("myDiv");
console.log(div.id);         // "myDiv""
console.log(div.className);  // "bd"
console.log(div.title);      // "Body text"
console.log(div.lang);       // "en"
console.log(div.dir);        // "ltr"

固然,像下面如许经由历程为每一个属性给予新的值,也可以修正对应的每一个特性:

div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir ="rtl";

并非对一切属性的修正都邑在页面中直观地表现出来。对 idlang 的修正对用户而言是通明不可见的(假定没有基于它们的值设置的 CSS 款式),而对 title 的修正则只会在鼠标移动到这个元素之上时才会显现出来。对 dir 的修正会在属性被重写的那一刻,立时影响页面中文本的左、右对齐体式格局。修正 className 时,假如新类关联了与此前差异的 CSS 款式,那末就会立时运用新的款式。

获得特性

每一个元素都有一或多个特性,这些特性的用处是给出响应元素或其内容的附加信息。操纵特性的 DOM 要领主要有三个,离别是 getAttribute()setAttribute()removeAttribute()。这三个要领可以针对任何特性运用,包括那些以 HTMLElement 范例属性的情势定义的特性。来看下面的例子:

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

注重,通报给 getAttribute() 的特性名与现实的特性名雷同。因而要想获得 class 特性值,应当传入 "class" 而不是 "className",后者只需在经由历程对象属性接见特性时才用。假如给定称号的特性不存在,getAttribute() 返回 null

经由历程 getAttribute() 要领也可以获得自定义特性(即范例 HTML 言语中没有的特性)的值,以下面的元素为例:

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

这个元素包括一个名为 my_special_attribute 的自定义特性,它的值是 "hello!"。可以像获得其他特性一样获得这个值,以下所示:

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

不过,特性的称号是不辨别大小写的,即 "ID""id" 代表的都是同一个特性。别的也要注重,依据 HTML5 范例,自定义特性应当加上 data- 前缀以便考证。

任何元素的一切特性,也都可以经由历程 DOM 元素自身的属性来接见。固然,HTMLElement 也会有5个属性与响应的特性一一对应。不过,只需公认的(非自定义的)特性才会以属性的情势增加到 DOM 对象中。以下面的元素为例:

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

因为 idalign 在 HTML 中是 div 的公认特性,因而该元素的 DOM 对象中也将存在对应的属性。不过,自定义特性 my_special_attribute 在 Safari、Opera、Chrome 及 Firefox 中是不存在的;但 IE 却会为自定义特性也建立属性,以下面的例子所示:

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

有两类特别的特性,它们虽然有对应的属性名,但属性的值与经由历程 getAttribute() 返回的值并不雷同。第一类特性就是 style,用于经由历程 CSS 为元素指定款式。在经由历程 getAttribute() 接见时,返回的 style 特性值中包括的是CSS文本,而经由历程属性来接见它则会返回一个对象。因为 style 属性是用于以编程体式格局接见元素款式的,因而并没有直接映射到 style 特性。

第二类异乎寻常的特性是 onclick 如许的事宜处置惩罚顺序。当在元素上运用时,onclick 特性中包括的是 JavaScript 代码,假如经由历程 getAttribute() 接见,则会返回响应代码的字符串。而在接见 onclick 属性时,则会返回一个 JavaScript 函数(假如未在元素中指定响应特性,则返回 null)。这是因为 onclick 及其他事宜处置惩罚顺序属性自身就应当被给予函数值。

因为存在这些差异,在经由历程 JavaScript 以编程体式格局操纵 DOM 时,开发人员常常不运用 getAttribute(),而是只运用对象的属性。只需在获得自定义特性值的状况下,才会运用 getAttribute() 要领。

设置特性

getAttribute() 对应的要领是 setAttribute(),这个要领接收两个参数:要设置的特性名和值。假如特性已存在,setAttribute() 会以指定的值替代现有的值;假如特性不存在,setAttribute() 则建立该属性并设置响应的值。来看下面的例子:

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

经由历程 setAttribute() 要领既可以操纵HTML特性也可以操纵自定义特性。经由历程这个要领设置的特性名会被一致转换为小写情势,即 "ID" 最终会变成 "id"

因为一切特性都是属性,所以直接给属性赋值可以设置特性的值,以下所示。

div.id = "someOtherId";
div.align = "left";

不过,像下面如许为 DOM 元素增加一个自定义的属性,该属性不会自动成为元素的特性。

div.mycolor = "red";
console.log(div.mycolor); // "red"
console.log(div.getAttribute("mycolor")); // null(IE除外)

这个例子增加了一个名为 mycolor 的属性并将它的值设置为 "red"。在大多半浏览器中,这个属性都不会自动变成元素的特性,因而想经由历程 getAttribute() 获得同名特性的值,效果会返回 null。但是,自定义属性在 IE 中会被看成元素的特性,反之亦然。

要引见的末了一个要领是 removeAttribute(),这个要领用于完整删除元素的特性。挪用这个要领不仅会消灭特性的值,而且也会从元素中完整删除特性,以下所示:

div.removeAttribute("class");

这个要领并不常常运用,但在序列化 DOM 元素时,可以经由历程它来确切地指定要包括哪些特性。

建立元素

运用 document.createElement() 要领可以建立新元素。这个要领只接收一个参数,即要建立元素的标署名。这个标署名在 HTML 文档中不辨别大小写。比方,运用下面的代码可以建立一个 div 元素。

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

在运用 createElement() 要领建立新元素的同时,也为新元素设置了 ownerDocuemnt 属性。此时,还可以操纵元素的特性,为它增加更多子节点,以及实行其他操纵。来看下面的例子。

div.id = "myNewDiv";
div.className = "box";

在新元素上设置这些特性只是给它们给予了响应的信息。因为新元素尚未被增加到文档树中,因而设置这些特性不会影响浏览器的显现。要把新元素增加到文档树,可以运用 appendChild()insertBefore()replaceChild() 要领。下面的代码会把新建立的元素增加到文档的 <body> 元素中。

document.body.appendChild(div);

一旦将元素增加到文档树中,浏览器就会立时显现该元素。今后,对这个元素所作的任何修正都邑及时反应在浏览器中。

元素的子节点

元素可以有恣意数量的子节点和子女节点,因为元素可以是其他元素的子节点。元素的 childNodes 属性中包括了它的一切子节点,这些子节点有多是元素、文本节点、诠释或处置惩罚指令。差异浏览器在对待这些节点方面存在明显的差异,以下面的代码为例。

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

假如是 IE8 来剖析这些代码,那末 <ul> 元素会有3个子节点,离别是3个 <li> 元素。但假如是在其他浏览器中,<ul> 元素都邑有7个元素,包括3个 <li> 元素和4个文本节点(示意 <li> 元素之间的空白符)。假如像下面如许将元素间的空白符删除,那末一切浏览器都邑返回雷同数量的子节点。

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

关于这段代码,<ul> 元素在任何浏览器中都邑包括3个子节点。假如须要经由历程 childNodes 属性遍历子节点,那末肯定不要遗忘浏览器间的这一差异。这意味着在实行某项操纵之前,一般都要先检查一下 nodeTpye 属性,以下面的例子所示。

for (var i=0, len=element.childNodes.length; i < len; i++){
    if (element.childNodes[i].nodeType == 1){
        //实行某些操纵
    }
}

这个例子会轮回遍历特定元素的每一个子节点,然后只在子节点的 nodeType 即是1(示意是元素节点)的状况下,才会实行某些操纵。

假如想经由历程某个特定的标署名获得子节点或子女节点该怎么办呢?现实上,元素也支撑 getElementsByTagName() 要领。在经由历程元素挪用这个要领时,除了搜刮出发点是当前元素以外,其他方面都跟经由历程 document 挪用这个要领雷同,因而效果只会返回当前元素的子女。比方,要想获得前面 <ul> 元素中包括的一切 <li> 元素,可以运用以下代码。

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

要注重的是,这里 <ul> 的子女中只包括直接子元素。不过,假如它包括更多条理的子女元素,那末各个条理中包括的 <li> 元素也都邑返回。

Text 范例

文本节点由 Text 范例示意,包括的是可以照字面诠释的纯文本内容。纯文本中可以包括转义后的 HTML 字符,但不能包括 HTML 代码。Text 节点具有以下特性:

  • nodeType 的值为3;

  • nodeName 的值为 "#text"

  • nodeValue 的值为节点所包括的文本;

  • parentNode 是一个 Element

  • 不支撑(没有)子节点。

可以经由历程 nodeValue 属性或 data 属性接见 Text 节点中包括的文本,这两个属性中包括的值雷同。对 nodeValue 的修正也会经由历程 data 反应出来,反之亦然。运用以下要领可以操纵节点中的文本。

  • 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* 为止处的字符串。

除了这些要领以外,文本节点另有一个 length 属性,保存着节点中字符的数量。而且,nodeValue.lengthdata.length 中也保存着一样的值。

在默许状况下,每一个可以包括内容的元素最多只能有一个文本节点,而且必需确切有内容存在。来看几个例子。

<!-- 没有内容,也就没有文本节点 -->
<div></div>

<!-- 有空格,因而有一个文本节点 -->
<div> </div>

<!-- 有内容,因而有一个文本节点 -->
<div>Hello World!</div>

上面代码给出的第一个 <div> 元素没有内容,因而也就不存在文本节点。最先与完毕标签之间只需存在内容,就会建立一个文本节点。因而,第二个 <div> 元素中虽然只包括一个空格,但仍然有一个文簿子节点;文本节点的 nodeValue 值是一个空格。第三个 div 也有一个文本节点,其 nodeValue 的值为 "Hello World!"。可以运用以下代码来接见这些文簿子节点。

var textNode = div.firstChild;  // 或许div.childNodes[0]

在获得了文本节点的援用后,就可以像下面如许来修正它了。

div.firstChild.nodeValue = "Some other message";

假如这个文本节点当前存在于文档树中,那末修正文本节点的效果就会立时获得反应。别的,在修正文本节点时还要注重,此时的字符串会经由 HTML(或XML,取决于文档范例)编码。换句话说,小于号、大于号或引号都邑像下面的例子一样被转义。

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

应当说,这是在向 DOM 文档中插进去文本之前,先对其举行 HTML 编码的一种有用体式格局。

在 IE8、Firefox、Safari、Chrome 和 Opera中,可以经由历程剧本接见 Text 范例的组织函数和原型。

建立文本节点

可以运用 document.createTextNode() 建立新文本节点,这个要领接收一个参数——要插进去节点中的文本。与设置已有文本节点的值一样,作为参数的文本也将根据 HTML 或 XML 的花样举行编码。

var textNode = document.createTextNode("<strong>Hello</strong> world!");

在建立新文本节点的同时,也会为其设置 ownerDocument 属性。不过,除非把新节点增加到文档树中已存在的节点中,不然我们不会在浏览器窗口中看到新节点。下面的代码会建立一个 <div> 元素并向个中增加一条音讯。

var element = document.createElement("div");
element.className = "message";

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

document.body.appendChild(element);

这个例子建立了一个新 <div> 元素并为它指定了值为 "message"class 特性。然后,又建立了一个文本节点,并将其增加到前面建立的元素中。末了一步,就是将这个元素增加到了文档的 <body> 元素中,如许就可以在浏览器中看到新建立的元素和文本节点了。

平常状况下,每一个元素只需一个文簿子节点。不过,在某些状况下也可以包括多个文簿子节点,以下面的例子所示。

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

假如两个文本节点是相邻的同胞节点,那末这两个节点中的文本就会连起来显现,中心不会有空格。

范例化文本节点

DOM 文档中存在相邻的同胞文本节点很轻易致使杂沓,因为分不清哪一个文本节点示意哪一个字符串。别的,DOM 文档中涌现相邻文本节点的状况也不在少数,于是就催生了一个可以将相邻文本节点兼并的要领。这个要领是由 Node 范例定义的(因而在一切节点范例中都存在),名叫 normalize()。假如在一个包括两个或多个文本节点的父元素上挪用 normalize() 要领,则会将一切文本节点兼并成一个节点,效果节点的 nodeValue 即是将兼并前每一个文本节点的 nodeValue 值拼接起来的值。来看一个例子。

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);
console.log(element.childNodes.length);    // 2

element.normalize();
console.log(element.childNodes.length);    // 1
console.log(element.firstChild.nodeValue); // "Hello world!Yippee!"

浏览器在剖析文档时永久不会建立相邻的文本节点。这类状况只会作为实行DOM操纵的效果涌现。

支解文本节点

Text 范例供应了一个作用与 normalize() 相反的要领 splitText()。这个要领会将一个文本节点分红两个文本节点,即根据指定的位置支解 nodeValue 值。本来的文本节点将包括从最先到指定位置之前的内容,新文本节点将包括剩下的文本。这个要领会返回一个新文本节点,该节点与原节点的 parentNode 雷同。

Comment 范例

诠释在 DOM 中是经由历程 Comment 范例来示意的。Comment 节点具有以下特性:

  • nodeType 的值为8;

  • nodeName 的值为 "#comment"

  • nodeValue 的值是诠释的内容;

  • parentNode 多是 DocumentElement

  • 不支撑(没有)子节点。

Comment 范例与 Text 范例继续自雷同的基类,因而它具有除splitText() 以外的一切字符串操纵要领。与 Text 范例相似,也可以经由历程 nodeValuedata 属性来获得诠释的内容。

诠释节点可以经由历程其父节点来接见,以下面的代码为例。

<div id="myDiv"><!--A comment --></div>

在此,诠释节点是 <div> 元素的一个子节点,因而可以经由历程下面的代码来接见它。

var div = document.getElementById("myDiv");
var comment = div.firstChild;
console.log(comment.data);    // "A comment"

别的,运用 document.createComment() 并为其通报诠释文本也可以建立诠释节点,以下面的例子所示。

var comment = document.createComment("A comment ");

明显,开发人员很少会建立和接见诠释节点,因为诠释节点对算法鲜有影响。另外,浏览器也不会辨认位于 </html> 标签背面的诠释。假如要接见诠释节点,肯定要保证它们位于 <html></html> 之间。

Attr 范例

元素的特性在 DOM 中以 Attr 范例来示意。在一切浏览器中(包括 IE8),都可以接见 Attr 范例的组织函数和原型。从手艺角度讲,特性就是存在于元素的 attributes 属性中的节点。特性节点具有以下特性:

  • nodeType 的值为11;

  • nodeName 的值是特性的称号;

  • nodeValue 的值是特性的值;

  • parentNode 的值为 null

  • 在 HTML 中不支撑(没有)子节点;

  • 在 XML 中子节点可以是 Text EntityReference

只管它们也是节点,但特性却不被认为是 DOM 文档树的一部份。开发人员最常运用的是 getAttribute()setAttribute()remveAttribute() 要领,很少直接援用特性节点。

Attr 对象有3个属性:namevaluespecified。个中,name 是特性称号(与 nodeName 的值雷同),value 是特性的值(与 nodeValue 的值雷同),而 specified 是一个布尔值,用以区分特性是在代码中指定的,照样默许的。

运用 document.createAttribute() 并传入特性的称号可以建立新的特性节点。比方,要为元素增加 align 特性,可以运用以下代码:

var attr = document.createAttribute("align");
attr.value = "left";
element.setAttributeNode(attr);
console.log(element.attributes["align"].value);       // "left"
console.log(element.getAttributeNode("align").value); // "left"
console.log(element.getAttribute("align"));           // "left"

增加特性以后,可以经由历程以下任何体式格局接见该特性:attributes 属性、getAttributeNode() 要领以及 getAttribute() 要领。个中,attributesgetAttributeNode() 都邑返回对应特性的 Attr 节点,而 getAttribute() 则只返回特性的值。

DOM 操纵

许多时候,DOM 操纵都比较简明,因而用 JavaScript 天生那些一般原本是用 HTML 代码天生的内容并不贫苦。不过,也有一些时候,操纵 DOM 并不像表面上看起来那末简朴。因为浏览器中充溢着隐蔽的圈套和不兼容题目,用 JavaScript 代码处置惩罚 DOM 的某些部份要比处置惩罚其他部份更庞杂一些。

动态剧本

运用 <script> 元素可以向页面中插进去 JavaScript 代码,一种体式格局是经由历程其 src 特性包括外部文件,另一种体式格局就是用这个元素自身来包括代码。而这一节要议论的动态剧本,指的是在页面加载时不存在,但未来的某一时候经由历程修正 DOM 动态增加的剧本。跟操纵 HTML 元素一样,建立动态剧本也有两种体式格局:插进去外部文件和直接插进去 JavaScript 代码。

动态加载的外部 JavaScript 文件可以立时运转,比以下面的 <script> 元素:

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

建立这个 DOM 节点的代码以下所示:

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

下面是挪用这个函数的示例:

loadScript("client.js");

另一种指定 JavaScript 代码的体式格局是行内体式格局,以下面的例子所示:

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

从逻辑上讲,下面操纵的 DOM 代码是有用的:

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

在 Firefox、Safari、Chrome 和 Opera 中,这些 DOM 代码可以一般运转。但在 IE 中,则会致使毛病。IE 将 <script> 视为一个特别的元素,不允许 DOM 接见其子节点。不过,可以运用 <script> 元素的text 属性来指定 JavaScript 代码,像下面的例子如许:

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');}");

以这类体式格局加载的代码会在全局作用域中实行,而且当剧本实行后将立时可用。现实上,如许实行代码与在全局作用域中把雷同的字符串通报给 eval() 是一样的。

动态款式

可以把 CSS 款式包括到 HTML 页面中的元素有两个。个中,<link> 元素用于包括来自外部的文件,而 <style> 元素用于指定嵌入的款式。与动态剧本相似,所谓动态款式是指在页面刚加载时不存在的款式;动态款式是在页面加载完成后动态增加到页面中的。

我们以下面这个典范的 <link> 元素为例:

<link rel="stylesheet" type="text/css" href="styles.css">

运用 DOM 代码可以很轻易地动态建立出这个元素:

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);
}

以上代码在一切主流浏览器中都可以一般运转。须要注重的是,必需将 <link> 元素增加到 <head> 而不是 <body> 元素,才保证在一切浏览器中的行动一致。挪用函数的代码以下所示:

loadStyles("styles.css");

加载外部款式文件的历程是异步的,也就是加载款式的历程没有牢固的序次。平常来说,知不知道款式已加载完成并不主要。

另一种定义款式的体式格局是运用 <style> 元夙来包括嵌入式 CSS,以下所示:

<style type="text/css">
body { background-color: red; }
</style>

根据雷同的逻辑,以下 DOM 代码应当是有用的:

var style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode("body{background-color:red}")); 
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);

以上代码可以在 Firefox、Safari、Chrome 和 Opera 中运转,在 IE 中则会报错。IE将 <style> 视为一个特别的、与 <script> 相似的节点,不允许接见其子节点。事实上,IE 此时抛出的毛病与向 <script> 元素增加子节点时抛出的毛病雷同。处理 IE 中这个题目的方法,就是接见元素的 styleSheet 属性,该属性又有一个 cssText 属性,可以接收 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}");

这类体式格局会及时地向页面中增加款式,因而可以立时看到变化。

运用 NodeList

明白 NodeList 及其“嫡亲” NamedNodeMapHTMLCollection,是从团体上透辟明白 DOM 的症结所在。这三个鸠合都是“动态的”;换句话说,每当文档构造发生变化时,它们都邑获得更新。因而,它们一直都邑保存着最新、最正确的信息。从实质上说,一切 NodeList 对象都是在接见 DOM 文档时及时运转的查询。比方,以下代码会致使无穷轮回:

var i,div,divs = document.getElementsByTagName("div");
for (i=0; i < divs.length; i++){
    div = document.createElement("div");
    document.body.appendChild(div);
}

第一行代码会获得文档中一切 <div> 元素的 HTMLCollection。因为这个鸠合是“动态的”,因而只需有新 <div> 元素被增加到页面中,这个元素也会被增加到该鸠合中。浏览器不会将建立的一切鸠合都保存在一个列表中,而是鄙人一次接见鸠应时再更新鸠合。效果,在碰到上例中所示的轮回代码时,就会致使一个风趣的题目。每次轮回都要对前提 i < divs.length 求值,意味着会运转获得一切 <div> 元素的查询。斟酌到轮回体每次都邑建立一个新 <div> 元素并将其增加到文档中,因而 divs.length 的值在每次轮回后都邑递增。既然idivs.length 每次都邑同时递增,效果它们的值永久也不会相称。

假如想要迭代一个 NodeList,最好是运用 length 属性初始化第二个变量,然后将迭代器与该变量举行比较,以下面的例子所示:

var i,len,div,divs = document.getElementsByTagName("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,都邑运转一次基于文档的查询。所以,可以斟酌将从 NodeList 中获得的值缓存起来。

小结

DOM 是言语中立的 API,用于接见和操纵 HTML 和 XML 文档。DOM1 级将 HTML 和 XML 文档抽象地看做一个条理化的节点树,可以运用 JavaScript 来操纵这个节点树,进而转变底层文档的表面和构造。

DOM 由种种节点组成,扼要总结以下。

  • 最基本的节点范例是 Node,用于抽象地示意文档中一个自力的部份;一切其他范例都继续自 Node

  • Document 范例示意全部文档,是一组分层节点的根节点。在 JavaScript 中,document 对象是 Document 的一个实例。运用 document 对象,有许多种体式格局可以查询和获得节点。

  • Element 节点示意文档中的一切 HTML 或 XML 元素,可以用来操纵这些元素的内容和特性。

  • 别的另有一些节点范例,离别示意文本内容、诠释、文档范例、CDATA 地区和文档片断。

接见 DOM 的操纵在多半状况下都很直观,不过在处置惩罚 <script><style> 元素时照样存在一些庞杂性。因为这两个元素离别包括剧本和款式信息,因而浏览器一般会将它们与其他元素区分对待。这些区分致使了在针对这些元素运用 innerHTML 时,以及在建立新元素时的一些题目。

明白 DOM 的症结,就是明白 DOM 对机能的影响。DOM 操纵往往是 JavaScript 顺序中开支最大的部份,而因接见 NodeList 致使的题目为最多。NodeList 对象都是“动态的”,这就意味着每次接见 NodeList 对象,都邑运转一次查询。有鉴于此,最好的方法就是只管削减 DOM 操纵。

关卡

细致想一想,下面代码块会输出什么效果呢?前3个应战比拟前一章节,代码仅仅是多了换行,效果会有什么不一样呢?

<!-- 应战一 -->
<body>
<div id = "t">
    <span>aaa</span>
    <span>bbb</span>
    <span>ccc</span>
</div>
</body>
<script> 
    var d = document.getElementById("t");  
    document.writeln(d.firstChild.innerHTML);  // ???
    document.writeln(d.lastChild.innerHTML);   // ???   
</script>
<!-- 应战二 -->
<body name="ddd">
<div id = "t">
    <span>aaa</span>
    <span>bbb</span>
    <span>ccc</span>
 </div>
</body>
<script> 
    var d = document.getElementById("t");  
    document.writeln(d.childNodes[1].innerHTML); // ???
    document.writeln(d.parentNode.getAttribute("name")); // ???
</script>
<!-- 应战三 -->
<body name="ddd">
<div id = "t">
    <span>aaa</span>
    <span>bbb</span>
    <span>ccc</span>
</div>
</body>
<script> 
    var d = document.getElementById("t").childNodes[1];  
    document.writeln(d.nextSibling.innerHTML);      // ???
    document.writeln(d.previousSibling.innerHTML);  // ???
</script>
<!-- 应战四 -->
<body>
    <div id="t" class="content" style="background: red;" wife="sophie" onclick="alert('123');"></div>
</body>
<script> 
    var t = document.getElementById("t");    
    console.log(t.class);                   // ???
    console.log(t.getAttribute("class"));   // ???
    console.log(t.className);               // ???
    console.log(t.getAttribute("className")); // ???
    console.log(t.style);                   // ???
    console.log(t.getAttribute("style"));   // ???
    console.log(t.style.background);        // ???
    console.log(t.getAttribute("style.background")); // ???
    console.log(t.wife);                    // ???
    console.log(t.getAttribute("wife"));    // ???
    console.log(t.onclick);                 // ???
    console.log(t.getAttribute("onclick")); // ???
</script>

更多

关注微信民众号「劼哥舍」复兴「答案」,猎取关卡详解。
关注 https://github.com/stone0090/javascript-lessons,猎取最新动态。

    原文作者:劼哥stone
    原文地址: https://segmentfault.com/a/1190000007566278
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞