Javascript 節(jié)點(diǎn)屬性:type,tag 和 content

2023-02-17 10:54 更新

讓我們更深入地了解一下 DOM 節(jié)點(diǎn)。

在本章中,我們將更深入地了解它們是什么,并學(xué)習(xí)它們最常用的屬性。

DOM 節(jié)點(diǎn)類

不同的 DOM 節(jié)點(diǎn)可能有不同的屬性。例如,標(biāo)簽 <a> 相對(duì)應(yīng)的元素節(jié)點(diǎn)具有鏈接相關(guān)的(link-related)屬性,標(biāo)簽 <input> 相對(duì)應(yīng)的元素節(jié)點(diǎn)具有與輸入相關(guān)的屬性,等。文本節(jié)點(diǎn)與元素節(jié)點(diǎn)不同。但是所有這些標(biāo)簽對(duì)應(yīng)的 DOM 節(jié)點(diǎn)之間也存在共有的屬性和方法,因?yàn)樗蓄愋偷?DOM 節(jié)點(diǎn)都形成了一個(gè)單一層次的結(jié)構(gòu)(single hierarchy)。

每個(gè) DOM 節(jié)點(diǎn)都屬于相應(yīng)的內(nèi)建類。

層次結(jié)構(gòu)(hierarchy)的根節(jié)點(diǎn)是 EventTargetNode 繼承自它,其他 DOM 節(jié)點(diǎn)繼承自 Node。

下圖做了進(jìn)一步說(shuō)明:


類如下所示:

  • EventTarget —— 是一切的根“抽象(abstract)”類。
  • 該類的對(duì)象從未被創(chuàng)建。它作為一個(gè)基礎(chǔ),以便讓所有 DOM 節(jié)點(diǎn)都支持所謂的“事件(event)”,我們會(huì)在之后學(xué)習(xí)它。

  • Node —— 也是一個(gè)“抽象”類,充當(dāng) DOM 節(jié)點(diǎn)的基礎(chǔ)。
  • 它提供了樹(shù)的核心功能:parentNode,nextSibling,childNodes 等(它們都是 getter)。Node 類的對(duì)象從未被創(chuàng)建。但是還有一些繼承自它的其他類(因此繼承了 Node 的功能)。

  • Document 由于歷史原因通常被 HTMLDocument 繼承(盡管最新的規(guī)范沒(méi)有規(guī)定)—— 是一個(gè)整體的文檔。
  • 全局變量 document 就是屬于這個(gè)類。它作為 DOM 的入口。

  • CharacterData —— 一個(gè)“抽象”類,被下述類繼承:
    • Text —— 對(duì)應(yīng)于元素內(nèi)部文本的類,例如 ?<p>Hello</p>? 中的 ?Hello?。
    • Comment —— 注釋類。它們不會(huì)被展示出來(lái),但每個(gè)注釋都會(huì)成為 DOM 中的一員。
  • Element —— 是 DOM 元素的基礎(chǔ)類。
  • 它提供了元素級(jí)導(dǎo)航(navigation),如 nextElementSibling,children,以及搜索方法,如 getElementsByTagName 和 querySelector。

    瀏覽器不僅支持 HTML,還支持 XML 和 SVG。因此,Element 類充當(dāng)?shù)氖歉唧w的類的基礎(chǔ):SVGElement,XMLElement(我們?cè)谶@里不需要它)和 HTMLElement。

  • 最后,HTMLElement —— 是所有 HTML 元素的基礎(chǔ)類。我們大部分時(shí)候都會(huì)用到它。
  • 它會(huì)被更具體的 HTML 元素繼承:

還有很多其他標(biāo)簽具有自己的類,可能還具有特定的屬性和方法,而一些元素,如 <span>、<section>、<article> 等,沒(méi)有任何特定的屬性,所以它們是 HTMLElement 類的實(shí)例。

因此,給定節(jié)點(diǎn)的全部屬性和方法都是繼承鏈的結(jié)果。

例如,我們考慮一下 <input> 元素的 DOM 對(duì)象。它屬于 HTMLInputElement 類。

它獲取屬性和方法,并將其作為下列類(按繼承順序列出)的疊加:

  • ?HTMLInputElement? —— 該類提供特定于輸入的屬性,
  • ?HTMLElement? —— 它提供了通用(common)的 HTML 元素方法(以及 getter 和 setter)
  • ?Element? —— 提供通用(generic)元素方法,
  • ?Node? —— 提供通用 DOM 節(jié)點(diǎn)屬性,
  • ?EventTarget? —— 為事件(包括事件本身)提供支持,
  • ……最后,它繼承自 ?Object?,因?yàn)橄?nbsp;?hasOwnProperty? 這樣的“普通對(duì)象”方法也是可用的。

我們可以通過(guò)回調(diào)來(lái)查看 DOM 節(jié)點(diǎn)類名,因?yàn)閷?duì)象通常都具有 constructor 屬性。它引用類的 constructor,constructor.name 就是它的名稱:

alert( document.body.constructor.name ); // HTMLBodyElement

……或者我們可以對(duì)其使用 toString 方法:

alert( document.body ); // [object HTMLBodyElement]

我們還可以使用 instanceof 來(lái)檢查繼承:

alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true

正如我們所看到的,DOM 節(jié)點(diǎn)是常規(guī)的 JavaScript 對(duì)象。它們使用基于原型的類進(jìn)行繼承。

在瀏覽器中,使用 console.dir(elem) 輸出元素來(lái)查看也是非常容易的。在控制臺(tái)中,你可以看到 HTMLElement.prototype 和 Element.prototype 等。

?console.dir(elem)? 與 ?console.log(elem)?

大多數(shù)瀏覽器在其開(kāi)發(fā)者工具中都支持這兩個(gè)命令:console.log 和 console.dir。它們將它們的參數(shù)輸出到控制臺(tái)中。對(duì)于 JavaScript 對(duì)象,這些命令通常做的是相同的事。

但對(duì)于 DOM 元素,它們是不同的:

  • ?console.log(elem)? 顯示元素的 DOM 樹(shù)。
  • ?console.dir(elem)? 將元素顯示為 DOM 對(duì)象,非常適合探索其屬性。

你可以在 document.body 上嘗試一下。

規(guī)范中的 IDL

在規(guī)范中,DOM 類不是使用 JavaScript 來(lái)描述的,而是一種特殊的 接口描述語(yǔ)言(Interface description language),簡(jiǎn)寫(xiě)為 IDL,它通常很容易理解。

在 IDL 中,所有屬性以其類型開(kāi)頭。例如,DOMString 和 boolean 等。

以下是摘錄(excerpt),并附有注釋:

// 定義 HTMLInputElement
// 冒號(hào) ":" 表示 HTMLInputElement 繼承自 HTMLElement
interface HTMLInputElement: HTMLElement {
  // 接下來(lái)是 <input> 元素的屬性和方法

  // "DOMString" 表示屬性的值是字符串
  attribute DOMString accept;
  attribute DOMString alt;
  attribute DOMString autocomplete;
  attribute DOMString value;

  // 布爾值屬性(true/false)
  attribute boolean autofocus;
  ...
  // 現(xiàn)在方法:"void" 表示方法沒(méi)有返回值
  void select();
  ...
}

“nodeType” 屬性

nodeType 屬性提供了另一種“過(guò)時(shí)的”用來(lái)獲取 DOM 節(jié)點(diǎn)類型的方法。

它有一個(gè)數(shù)值型值(numeric value):

  • 對(duì)于元素節(jié)點(diǎn) ?elem.nodeType == 1?,
  • 對(duì)于文本節(jié)點(diǎn) ?elem.nodeType == 3?,
  • 對(duì)于 document 對(duì)象 ?elem.nodeType == 9?,
  • 在 規(guī)范 中還有一些其他值。

例如:

<body>
  <script>
  let elem = document.body;

  // 讓我們檢查一下:elem 中的節(jié)點(diǎn)類型是什么?
  alert(elem.nodeType); // 1 => element

  // 它的第一個(gè)子節(jié)點(diǎn)的類型是……
  alert(elem.firstChild.nodeType); // 3 => text

  // 對(duì)于 document 對(duì)象,類型是 9
  alert( document.nodeType ); // 9
  </script>
</body>

在現(xiàn)代腳本中,我們可以使用 instanceof 和其他基于類的檢查方法來(lái)查看節(jié)點(diǎn)類型,但有時(shí) nodeType 可能更簡(jiǎn)單。我們只能讀取 nodeType 而不能修改它。

標(biāo)簽:nodeName 和 tagName

給定一個(gè) DOM 節(jié)點(diǎn),我們可以從 nodeName 或者 tagName 屬性中讀取它的標(biāo)簽名:

例如:

alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY

tagName 和 nodeName 之間有什么不同嗎?

當(dāng)然,差異就體現(xiàn)在它們的名字上,但確實(shí)有些微妙。

  • ?tagName? 屬性僅適用于 ?Element? 節(jié)點(diǎn)。
  • ?nodeName? 是為任意 ?Node? 定義的:
    • 對(duì)于元素,它的意義與 ?tagName? 相同。
    • 對(duì)于其他節(jié)點(diǎn)類型(text,comment 等),它擁有一個(gè)對(duì)應(yīng)節(jié)點(diǎn)類型的字符串。

換句話說(shuō),tagName 僅受元素節(jié)點(diǎn)支持(因?yàn)樗鹪从?nbsp;Element 類),而 nodeName 則可以說(shuō)明其他節(jié)點(diǎn)類型。

例如,我們比較一下 document 的 tagName 和 nodeName,以及一個(gè)注釋節(jié)點(diǎn):

<body><!-- comment -->

  <script>
    // for comment
    alert( document.body.firstChild.tagName ); // undefined(不是一個(gè)元素)
    alert( document.body.firstChild.nodeName ); // #comment

    // for document
    alert( document.tagName ); // undefined(不是一個(gè)元素)
    alert( document.nodeName ); // #document
  </script>
</body>

如果我們只處理元素,那么 tagName 和 nodeName 這兩種方法,我們都可以使用,沒(méi)有區(qū)別。

標(biāo)簽名稱始終是大寫(xiě)的,除非是在 XML 模式下

瀏覽器有兩種處理文檔(document)的模式:HTML 和 XML。通常,HTML 模式用于網(wǎng)頁(yè)。只有在瀏覽器接收到帶有 Content-Type: application/xml+xhtml header 的 XML-document 時(shí),XML 模式才會(huì)被啟用。

在 HTML 模式下,tagName/nodeName 始終是大寫(xiě)的:它是 BODY,而不是 <body> 或 <BoDy>。

在 XML 模式中,大小寫(xiě)保持為“原樣”。如今,XML 模式很少被使用。

innerHTML:內(nèi)容

innerHTML 屬性允許將元素中的 HTML 獲取為字符串形式。

我們也可以修改它。因此,它是更改頁(yè)面最有效的方法之一。

下面這個(gè)示例顯示了 document.body 中的內(nèi)容,然后將其完全替換:

<body>
  <p>A paragraph</p>
  <div>A div</div>

  <script>
    alert( document.body.innerHTML ); // 讀取當(dāng)前內(nèi)容
    document.body.innerHTML = 'The new BODY!'; // 替換它
  </script>

</body>

我們可以嘗試插入無(wú)效的 HTML,瀏覽器會(huì)修復(fù)我們的錯(cuò)誤:

<body>

  <script>
    document.body.innerHTML = '<b>test'; // 忘記閉合標(biāo)簽
    alert( document.body.innerHTML ); // <b>test</b>(被修復(fù)了)
  </script>

</body>

腳本不會(huì)執(zhí)行

如果 innerHTML 將一個(gè) <script> 標(biāo)簽插入到 document 中 —— 它會(huì)成為 HTML 的一部分,但是不會(huì)執(zhí)行。

小心:“innerHTML+=” 會(huì)進(jìn)行完全重寫(xiě)

我們可以使用 elem.innerHTML+="more html" 將 HTML 附加到元素上。

就像這樣:

chatDiv.innerHTML += "<div>Hello<img src='smile.gif'/> !</div>";
chatDiv.innerHTML += "How goes?";

但我們必須非常謹(jǐn)慎地使用它,因?yàn)槲覀兯龅?nbsp;不是 附加內(nèi)容,而且完全地重寫(xiě)。

從技術(shù)上來(lái)說(shuō),下面這兩行代碼的作用相同:

elem.innerHTML += "...";
// 進(jìn)行寫(xiě)入的一種更簡(jiǎn)短的方式:
elem.innerHTML = elem.innerHTML + "..."

換句話說(shuō),innerHTML+= 做了以下工作:

  1. 移除舊的內(nèi)容。
  2. 然后寫(xiě)入新的 ?innerHTML?(新舊結(jié)合)。

因?yàn)閮?nèi)容已“歸零”并從頭開(kāi)始重寫(xiě),因此所有的圖片和其他資源都將重寫(xiě)加載。

在上面的 chatDiv 示例中,chatDiv.innerHTML+="How goes?" 重建了 HTML 內(nèi)容并重新加載了 smile.gif(希望它是緩存的)。如果 chatDiv 有許多其他文本和圖片,那么就很容易看到重新加載(譯注:是指在有很多內(nèi)容時(shí),重新加載會(huì)耗費(fèi)更多的時(shí)間,所以你就很容易看見(jiàn)頁(yè)面重載的過(guò)程)。

并且還會(huì)有其他副作用。例如,如果現(xiàn)有的文本被用鼠標(biāo)選中了,那么大多數(shù)瀏覽器都會(huì)在重寫(xiě) innerHTML 時(shí)刪除選定狀態(tài)。如果這里有一個(gè)帶有用戶輸入的文本的 <input>,那么這個(gè)被輸入的文本將會(huì)被移除。諸如此類。

幸運(yùn)的是,除了 innerHTML,還有其他可以添加 HTML 的方法,我們很快就會(huì)學(xué)到。

outerHTML:元素的完整 HTML

outerHTML 屬性包含了元素的完整 HTML。就像 innerHTML 加上元素本身一樣。

下面是一個(gè)示例:

<div id="elem">Hello <b>World</b></div>

<script>
  alert(elem.outerHTML); // <div id="elem">Hello <b>World</b></div>
</script>

注意:與 innerHTML 不同,寫(xiě)入 outerHTML 不會(huì)改變?cè)?。而是?DOM 中替換它。

是的,聽(tīng)起來(lái)很奇怪,它確實(shí)很奇怪,這就是為什么我們?cè)谶@里對(duì)此做了一個(gè)單獨(dú)的注釋。看一下。

考慮下面這個(gè)示例:

<div>Hello, world!</div>

<script>
  let div = document.querySelector('div');

  // 使用 <p>...</p> 替換 div.outerHTML
  div.outerHTML = '<p>A new element</p>'; // (*)

  // 蛤!'div' 還是原來(lái)那樣!
  alert(div.outerHTML); // <div>Hello, world!</div> (**)
</script>

看起來(lái)真的很奇怪,對(duì)吧?

在 (*) 行,我們使用 <p>A new element</p> 替換 div。在外部文檔(DOM)中我們可以看到的是新內(nèi)容而不是 <div>。但是正如我們?cè)?nbsp;(**) 行所看到的,舊的 div 變量并沒(méi)有被改變。

outerHTML 賦值不會(huì)修改 DOM 元素(在這個(gè)例子中是被 ‘div’ 引用的對(duì)象),而是將其從 DOM 中刪除并在其位置插入新的 HTML。

所以,在 div.outerHTML=... 中發(fā)生的事情是:

  • ?div? 被從文檔(document)中移除。
  • 另一個(gè) HTML 片段 ?<p>A new element</p>? 被插入到其位置上。
  • ?div? 仍擁有其舊的值。新的 HTML 沒(méi)有被賦值給任何變量。

在這兒很容易出錯(cuò):修改 div.outerHTML 然后繼續(xù)使用 div,就好像它包含的是新內(nèi)容一樣。但事實(shí)并非如此。這樣的東西對(duì)于 innerHTML 是正確的,但是對(duì)于 outerHTML 卻不正確。

我們可以向 elem.outerHTML 寫(xiě)入內(nèi)容,但是要記住,它不會(huì)改變我們所寫(xiě)的元素(‘elem’)。而是將新的 HTML 放在其位置上。我們可以通過(guò)查詢 DOM 來(lái)獲取對(duì)新元素的引用。

nodeValue/data:文本節(jié)點(diǎn)內(nèi)容

innerHTML 屬性僅對(duì)元素節(jié)點(diǎn)有效。

其他節(jié)點(diǎn)類型,例如文本節(jié)點(diǎn),具有它們的對(duì)應(yīng)項(xiàng):nodeValue 和 data 屬性。這兩者在實(shí)際使用中幾乎相同,只有細(xì)微規(guī)范上的差異。因此,我們將使用 data,因?yàn)樗獭?

讀取文本節(jié)點(diǎn)和注釋節(jié)點(diǎn)的內(nèi)容的示例:

<body>
  Hello
  <!-- Comment -->
  <script>
    let text = document.body.firstChild;
    alert(text.data); // Hello

    let comment = text.nextSibling;
    alert(comment.data); // Comment
  </script>
</body>

對(duì)于文本節(jié)點(diǎn),我們可以想象讀取或修改它們的原因,但是注釋呢?

有時(shí),開(kāi)發(fā)者會(huì)將信息或模板說(shuō)明嵌入到 HTML 中的注釋中,如下所示:

<!-- if isAdmin -->
  <div>Welcome, Admin!</div>
<!-- /if -->

……然后,JavaScript 可以從 data 屬性中讀取它,并處理嵌入的指令。

textContent:純文本

textContent 提供了對(duì)元素內(nèi)的 文本 的訪問(wèn)權(quán)限:僅文本,去掉所有 <tags>。

例如:

<div id="news">
  <h1>Headline!</h1>
  <p>Martians attack people!</p>
</div>

<script>
  // Headline! Martians attack people!
  alert(news.textContent);
</script>

正如我們所看到,只返回文本,就像所有 <tags> 都被剪掉了一樣,但實(shí)際上其中的文本仍然存在。

在實(shí)際開(kāi)發(fā)中,用到這樣的文本讀取的場(chǎng)景非常少。

寫(xiě)入 textContent 要有用得多,因?yàn)樗试S以“安全方式”寫(xiě)入文本。

假設(shè)我們有一個(gè)用戶輸入的任意字符串,我們希望將其顯示出來(lái)。

  • 使用 ?innerHTML?,我們將其“作為 HTML”插入,帶有所有 HTML 標(biāo)簽。
  • 使用 ?textContent?,我們將其“作為文本”插入,所有符號(hào)(symbol)均按字面意義處理。

比較兩者:

<div id="elem1"></div>
<div id="elem2"></div>

<script>
  let name = prompt("What's your name?", "<b>Winnie-the-Pooh!</b>");

  elem1.innerHTML = name;
  elem2.textContent = name;
</script>
  1. 第一個(gè) ?<div>? 獲取 name “作為 HTML”:所有標(biāo)簽都變成標(biāo)簽,所以我們可以看到粗體的 name。
  2. 第二個(gè) ?<div>? 獲取 name “作為文本”,因此我們可以從字面上看到 ?<b>Winnie-the-Pooh!</b>?。

在大多數(shù)情況下,我們期望來(lái)自用戶的文本,并希望將其視為文本對(duì)待。我們不希望在我們的網(wǎng)站中出現(xiàn)意料不到的 HTML。對(duì) textContent 的賦值正好可以做到這一點(diǎn)。

“hidden” 屬性

“hidden” 特性(attribute)和 DOM 屬性(property)指定元素是否可見(jiàn)。

我們可以在 HTML 中使用它,或者使用 JavaScript 對(duì)其進(jìn)行賦值,如下所示:

<div>Both divs below are hidden</div>

<div hidden>With the attribute "hidden"</div>

<div id="elem">JavaScript assigned the property "hidden"</div>

<script>
  elem.hidden = true;
</script>

從技術(shù)上來(lái)說(shuō),hidden 與 style="display:none" 做的是相同的事。但 hidden 寫(xiě)法更簡(jiǎn)潔。

這里有一個(gè) blinking 元素:

<div id="elem">A blinking element</div>

<script>
  setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>

更多屬性

DOM 元素還有其他屬性,特別是那些依賴于 class 的屬性:

  • ?value? —— ?<input>?,?<select>? 和 ?<textarea>?(?HTMLInputElement?,?HTMLSelectElement?……)的 value。
  • ?href? —— ?<a href="...">?(?HTMLAnchorElement?)的 href。
  • ?id? —— 所有元素(?HTMLElement?)的 “id” 特性(attribute)的值。
  • ……以及更多其他內(nèi)容……

例如:

<input type="text" id="elem" value="value">

<script>
  alert(elem.type); // "text"
  alert(elem.id); // "elem"
  alert(elem.value); // value
</script>

大多數(shù)標(biāo)準(zhǔn) HTML 特性(attribute)都具有相應(yīng)的 DOM 屬性,我們可以像這樣訪問(wèn)它。

如果我們想知道給定類的受支持屬性的完整列表,我們可以在規(guī)范中找到它們。例如,在 https://html.spec.whatwg.org/#htmlinputelement 中記錄了 HTMLInputElement。

或者,如果我們想要快速獲取它們,或者對(duì)具體的瀏覽器規(guī)范感興趣 — 我們總是可以使用 console.dir(elem) 輸出元素并讀取其屬性?;蛘咴跒g覽器的開(kāi)發(fā)者工具的元素(Elements)標(biāo)簽頁(yè)中探索“DOM 屬性”。

總結(jié)

每個(gè) DOM 節(jié)點(diǎn)都屬于一個(gè)特定的類。這些類形成層次結(jié)構(gòu)(hierarchy)。完整的屬性和方法集是繼承的結(jié)果。

主要的 DOM 節(jié)點(diǎn)屬性有:

?nodeType ?

我們可以使用它來(lái)查看節(jié)點(diǎn)是文本節(jié)點(diǎn)還是元素節(jié)點(diǎn)。它具有一個(gè)數(shù)值型值(numeric value):?1? 表示元素,?3? 表示文本節(jié)點(diǎn),其他一些則代表其他節(jié)點(diǎn)類型。只讀。

?nodeName/tagName ?

用于元素名,標(biāo)簽名(除了 XML 模式,都要大寫(xiě))。對(duì)于非元素節(jié)點(diǎn),?nodeName? 描述了它是什么。只讀。

?innerHTML ?

元素的 HTML 內(nèi)容??梢员恍薷摹?/p>

?outerHTML ?

元素的完整 HTML。對(duì) ?elem.outerHTML? 的寫(xiě)入操作不會(huì)觸及 ?elem? 本身。而是在外部上下文中將其替換為新的 HTML。

?nodeValue/data ?

非元素節(jié)點(diǎn)(文本、注釋)的內(nèi)容。兩者幾乎一樣,我們通常使用 ?data???梢员恍薷摹?/p>

?textContent ?

元素內(nèi)的文本:HTML 減去所有 ?<tags>?。寫(xiě)入文本會(huì)將文本放入元素內(nèi),所有特殊字符和標(biāo)簽均被視為文本??梢园踩夭迦胗脩羯傻奈谋?,并防止不必要的 HTML 插入。

?hidden ?

當(dāng)被設(shè)置為 ?true? 時(shí),執(zhí)行與 CSS ?display:none? 相同的事。

DOM 節(jié)點(diǎn)還具有其他屬性,具體有哪些屬性則取決于它們的類。例如,<input> 元素(HTMLInputElement)支持 valuetype,而 <a> 元素(HTMLAnchorElement)則支持 href 等。大多數(shù)標(biāo)準(zhǔn) HTML 特性(attribute)都具有相應(yīng)的 DOM 屬性。

然而,但是 HTML 特性(attribute)和 DOM 屬性(property)并不總是相同的,我們將在下一章中看到。

任務(wù)


計(jì)數(shù)后代

重要程度: 5

這里有一個(gè)樹(shù)結(jié)構(gòu)嵌套的 ul/li。

編寫(xiě)代碼,為每個(gè) <li> 顯示:

  1. 里面的文本內(nèi)容是什么(沒(méi)有子樹(shù))
  2. 嵌套的 ?<li>? 的數(shù)量 —— 所有后代,包括深層嵌套的后代。

在新窗口中演示

打開(kāi)一個(gè)任務(wù)沙箱。


解決方案

我們使用循環(huán)遍歷 <li>

for (let li of document.querySelectorAll('li')) {
  ...
}

循環(huán)時(shí),我們需要獲取每個(gè) li 中的文本。

我們可以從 li 的第一個(gè)子節(jié)點(diǎn)讀取文本,即文本節(jié)點(diǎn):

for (let li of document.querySelectorAll('li')) {
  let title = li.firstChild.data;

  // title 是在 <li> 中的任何其他節(jié)點(diǎn)之前的文本
}

然后我們就可以使用 li.getElementsByTagName('li') 來(lái)獲取后代的數(shù)目了。

使用沙箱打開(kāi)解決方案。


nodeType 中是什么?

重要程度: 5

下面這個(gè)腳本會(huì)顯示什么?

<html>

<body>
  <script>
    alert(document.body.lastChild.nodeType);
  </script>
</body>

</html>

解決方案

這里有一個(gè)陷阱。

在 <script> 執(zhí)行時(shí),最后一個(gè) DOM 節(jié)點(diǎn)就是 <script>,因?yàn)闉g覽器還沒(méi)有處理頁(yè)面的其余部分。

所以結(jié)果是 1(元素節(jié)點(diǎn))。

<html>

<body>
  <script>
    alert(document.body.lastChild.nodeType);
  </script>
</body>

</html>

注釋中的標(biāo)簽

重要程度: 3

這段代碼會(huì)顯示什么?

<script>
  let body = document.body;

  body.innerHTML = "<!--" + body.tagName + "-->";

  alert( body.firstChild.data ); // 這里會(huì)顯示什么?
</script>

解決方案

答案:BODY。

<script>
  let body = document.body;

  body.innerHTML = "<!--" + body.tagName + "-->";

  alert( body.firstChild.data ); // BODY
</script>

讓我們一步一步來(lái)看看發(fā)生了什么:

  1. ?<body>? 中的內(nèi)容被注釋所取代。注釋為 ?<!--BODY-->?,因?yàn)?nbsp;?body.tagName == "BODY"?。正如我們所記得的,在 HTML 模式下,?tagName? 總是大寫(xiě)的。
  2. 現(xiàn)在這個(gè)注釋是唯一的子節(jié)點(diǎn),所以我們?cè)?nbsp;?body.firstChild? 中獲取了它。
  3. 注釋的 ?data? 屬性是它的內(nèi)容(在 ?<!--...-->? 內(nèi)的):?"BODY"?。

層次結(jié)構(gòu)中的 "document" 在哪里?

重要程度: 4

document 屬于哪一類?

它位于 DOM 層次結(jié)構(gòu)(hierarchy)中的什么位置?

它繼承自 Node 還是 Element,或者可能是 HTMLElement


解決方案

我們可以通過(guò)輸出它,來(lái)看看它是屬于哪個(gè)類的,像這樣:

alert(document); // [object HTMLDocument]

或者:

alert(document.constructor.name); // HTMLDocument

因此,document 是 HTMLDocument 類的一個(gè)實(shí)例。

它位于 DOM 層次結(jié)構(gòu)(hierarchy)中的什么位置?

是的,我們可以瀏覽規(guī)范,但是手動(dòng)找出它會(huì)更快。

我們通過(guò) __proto__ 來(lái)遍歷原型鏈。

正如我們所知道的,類的方法在 constructor 的 prototype 中。例如,HTMLDocument.prototype 有用于文檔(document)的方法。

此外,在 prototype 中還有一個(gè)對(duì)構(gòu)造函數(shù)的引用:

alert(HTMLDocument.prototype.constructor === HTMLDocument); // true

為了以字符串的形式獲取一個(gè)類的 name,我們可以使用 constructor.name。讓我們對(duì)整個(gè) document 的原型鏈執(zhí)行該操作,直到 class Node

alert(HTMLDocument.prototype.constructor.name); // HTMLDocument
alert(HTMLDocument.prototype.__proto__.constructor.name); // Document
alert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node

這就是層次結(jié)構(gòu)。

我們還可以使用 console.dir(document) 來(lái)檢查對(duì)象,并通過(guò)打開(kāi) __proto__ 來(lái)查看這些名稱??刂婆_(tái)將它們從 constructor 內(nèi)部取出來(lái)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)