操作文件

在編寫網頁和應用程式時,您最常想要做的事情之一是以某種方式操作文件結構。 這通常透過使用文件物件模型 (DOM) 來完成,DOM 是用於控制 HTML 和樣式資訊的 API 集合,它大量使用了 Document 物件。 在本文中,我們將詳細介紹如何使用 DOM,以及其他一些有趣的 API,這些 API 可以以有趣的方式改變您的環境。

先決條件 對 HTML、CSS 和 JavaScript 的基本瞭解——包括 JavaScript 物件。
目標 熟悉核心 DOM API,以及與 DOM 和文件操作相關的其他 API。

網頁瀏覽器的重要部分

Web 瀏覽器是非常複雜的軟體,有很多活動部件,其中許多不能由使用 JavaScript 的 Web 開發人員控制或操作。 您可能認為這些限制是一件壞事,但瀏覽器被鎖定是有充分理由的,主要原因是安全。 想象一下,如果一個網站可以訪問您儲存的密碼或其他敏感資訊,並以您的身份登入網站?

儘管存在這些限制,Web API 仍然為我們提供了訪問許多功能,使我們能夠使用網頁做很多事情。 您會在程式碼中經常引用一些非常明顯的片段——請考慮以下圖表,它代表了瀏覽器中直接參與檢視網頁的主要部分

Important parts of web browser; the document is the web page. The window includes the entire document and also the tab. The navigator is the browser, which includes the window (which includes the document) and all other windows.

  • 視窗是載入網頁的瀏覽器選項卡;這在 JavaScript 中由 Window 物件表示。 使用此物件上可用的方法,您可以執行諸如返回視窗大小(參見 Window.innerWidthWindow.innerHeight)、操作載入到該視窗的文件、在客戶端儲存特定於該文件的資料(例如使用本地資料庫或其他儲存機制)、將 事件處理程式 附加到當前視窗等等。
  • 導航器代表瀏覽器的狀態和身份(即使用者代理),因為它存在於網路上。 在 JavaScript 中,這由 Navigator 物件表示。 您可以使用此物件檢索諸如使用者首選語言、來自使用者網路攝像頭的媒體流等內容。
  • 文件(由瀏覽器中的 DOM 表示)是載入到視窗中的實際頁面,在 JavaScript 中由 Document 物件表示。 您可以使用此物件返回和操作構成文件的 HTML 和 CSS 中的資訊,例如獲取對 DOM 中元素的引用、更改其文字內容、對其應用新樣式、建立新元素並將它們新增到當前元素作為子元素,甚至完全刪除它。

在本文中,我們將主要關注操作文件,但我們也會展示一些其他有用的內容。

文件物件模型

您每個瀏覽器選項卡中當前載入的文件都由文件物件模型表示。 這是一個由瀏覽器建立的“樹狀結構”表示,它使 HTML 結構能夠被程式語言輕鬆訪問——例如,瀏覽器本身使用它在渲染頁面時將樣式和其他資訊應用於正確的元素,並且像您這樣的開發人員可以在頁面渲染後使用 JavaScript 操作 DOM。

我們在 dom-example.html (也可以線上檢視) 建立了一個簡單的示例頁面。 嘗試在您的瀏覽器中開啟它——它是一個非常簡單的頁面,包含一個 <section> 元素,您可以在其中找到一個影像,以及一個包含連結的段落。 HTML 原始碼如下所示

html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>Simple DOM example</title>
  </head>
  <body>
    <section>
      <img
        src="dinosaur.png"
        alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth." />
      <p>
        Here we will add a link to the
        <a href="https://www.mozilla.org/">Mozilla homepage</a>
      </p>
    </section>
  </body>
</html>

另一方面,DOM 看起來像這樣

Tree structure representation of Document Object Model: The top node is the doctype and HTML element. Child nodes of the HTML include head and body. Each child element is a branch. All text, even white space, is shown as well.

注意: 此 DOM 樹圖使用 Ian Hickson 的 即時 DOM 檢視器 建立。

樹中的每個條目都稱為節點。 您可以在上面的圖中看到,一些節點表示元素(標識為 HTMLHEADMETA 等),而其他節點表示文字(標識為 #text)。 還存在 其他型別的節點,但這些是您會遇到的主要節點。

節點也以它們在樹中相對於其他節點的位置來指代

  • 根節點:樹中最頂部的節點,在 HTML 的情況下始終是 HTML 節點(其他標記詞彙表,如 SVG 和自定義 XML 將具有不同的根元素)。
  • 子節點直接位於另一個節點內的節點。 例如,IMG 是上面示例中 SECTION 的子節點。
  • 後代節點任何地方位於另一個節點內的節點。 例如,IMG 是上面示例中 SECTION 的子節點,它也是一個後代。 IMG 不是 BODY 的子節點,因為它在樹中低於 BODY 兩級,但它是 BODY 的後代。
  • 父節點:在其中包含另一個節點的節點。 例如,BODY 是上面示例中 SECTION 的父節點。
  • 兄弟節點:位於 DOM 樹中同一級別的節點。 例如,IMGP 是上面示例中的兄弟節點。

在使用 DOM 之前熟悉這些術語很有用,因為您會遇到的一些程式碼術語使用它們。 如果您學習過 CSS(例如,後代選擇器、子選擇器),您可能也已經遇到過它們。

主動學習:基本 DOM 操作

為了開始學習 DOM 操作,讓我們從一個實際示例開始。

  1. 獲取 dom-example.html 頁面 和與其相關的 影像 的本地副本。
  2. 在結束的 </body> 標記之前新增一個 <script></script> 元素。
  3. 要操作 DOM 內部的元素,您首先需要選擇它並將其引用儲存在一個變數中。 在您的指令碼元素中,新增以下行
    js
    const link = document.querySelector("a");
    
  4. 現在我們已經將元素引用儲存在變數中,我們可以開始使用可用的屬性和方法對其進行操作(這些屬性和方法在介面上定義,例如 HTMLAnchorElement<a> 元素的情況下,其更通用的父介面 HTMLElement,以及 Node——它表示 DOM 中的所有節點)。 首先,讓我們透過更新 Node.textContent 屬性的值來更改連結內的文字。 在前一行下方新增以下行
    js
    link.textContent = "Mozilla Developer Network";
    
  5. 我們還應該更改連結指向的 URL,以便在單擊它時不會轉到錯誤的位置。 新增以下行,同樣位於底部
    js
    link.href = "https://mdn.club.tw";
    

請注意,與 JavaScript 中的許多事情一樣,有許多方法可以選擇元素並將對其的引用儲存在一個變數中。 Document.querySelector() 是推薦的現代方法。 它很方便,因為它允許您使用 CSS 選擇器選擇元素。 上面的 querySelector() 呼叫將匹配出現在文件中的第一個 <a> 元素。 如果您想匹配並對多個元素執行操作,您可以使用 Document.querySelectorAll(),它匹配文件中與選擇器匹配的每個元素,並將對其的引用儲存在稱為 陣列 類物件的 NodeList 中。

還有一些較舊的方法可用於獲取元素引用,例如

  • Document.getElementById(),它選擇具有給定 id 屬性值的元素,例如 <p id="myId">My paragraph</p>。 ID 作為引數傳遞給函式,即 const elementRef = document.getElementById('myId')
  • Document.getElementsByTagName(),它返回一個數組類物件,其中包含頁面上給定型別的所有元素,例如 <p><a> 等。 元素型別作為引數傳遞給函式,即 const elementRefArray = document.getElementsByTagName('p')

這兩個方法在舊瀏覽器中的效果比 querySelector() 等現代方法更好,但不如它們方便。 看看您還能找到哪些其他方法!

建立和放置新節點

上面已經讓您對可以做些什麼有了一些瞭解,但讓我們更進一步,看看如何建立新元素。

  1. 回到當前示例,讓我們先獲取對 <section> 元素的引用——在現有指令碼的底部新增以下程式碼(對其他行也執行相同的操作)
    js
    const sect = document.querySelector("section");
    
  2. 現在,讓我們使用 Document.createElement() 建立一個新的段落,並像以前一樣賦予它一些文字內容
    js
    const para = document.createElement("p");
    para.textContent = "We hope you enjoyed the ride.";
    
  3. 您現在可以使用 Node.appendChild() 將新段落追加到該部分的末尾
    js
    sect.appendChild(para);
    
  4. 最後,對於這部分,讓我們在連結所在的段落中新增一個文字節點,以很好地結束句子。 首先,我們將使用 Document.createTextNode() 建立文字節點
    js
    const text = document.createTextNode(
      " — the premier source for web development knowledge.",
    );
    
  5. 現在我們將獲取對連結所在的段落的引用,並將文字節點追加到它
    js
    const linkPara = document.querySelector("p");
    linkPara.appendChild(text);
    

這些就是將節點新增到 DOM 所需的大部分內容——您在構建動態介面時將大量使用這些方法(我們將在後面介紹一些示例)。

移動和刪除元素

有時您可能想要移動節點,或完全從 DOM 中刪除它們。 這是完全可能的。

如果我們想將包含連結的段落移動到該部分的底部,我們可以這樣做

js
sect.appendChild(linkPara);

這將段落移動到該部分的底部。 您可能認為它會建立第二個副本,但事實並非如此——linkPara 是該段落唯一副本的引用。 如果您想建立一個副本並將其新增,則需要使用 Node.cloneNode()

刪除節點也很簡單,至少當您有要刪除節點及其父節點的引用時。 在我們當前的情況下,我們只需使用 Node.removeChild(),如下所示

js
sect.removeChild(linkPara);

當您只想根據對節點本身的引用刪除節點時,這很常見,您可以使用 Element.remove()

js
linkPara.remove();

此方法在舊瀏覽器中不受支援。 它們沒有方法告訴節點刪除自身,因此您需要執行以下操作。

js
linkPara.parentNode.removeChild(linkPara);

嘗試將上述程式碼行新增到您的程式碼中。

操作樣式

可以使用多種方法透過 JavaScript 操作 CSS 樣式。

首先,您可以使用 Document.stylesheets 獲取附加到文件的所有樣式表列表,該方法返回包含 CSSStyleSheet 物件的類似陣列的物件。然後,您可以根據需要新增或刪除樣式。但是,我們不會詳細介紹這些功能,因為它們是一種比較古老且難以操作樣式的方法。還有更簡單的方法。

第一個方法是直接將內聯樣式新增到您想要動態設定樣式的元素。這可以透過 HTMLElement.style 屬性完成,該屬性包含文件中每個元素的內聯樣式資訊。您可以設定此物件的屬性以直接更新元素樣式。

  1. 例如,嘗試將以下幾行新增到我們的示例中
    js
    para.style.color = "white";
    para.style.backgroundColor = "black";
    para.style.padding = "10px";
    para.style.width = "250px";
    para.style.textAlign = "center";
    
  2. 重新載入頁面,您將看到樣式已應用於段落。如果您在瀏覽器的 頁面檢查器/DOM 檢查器 中檢視該段落,您將看到這些行確實向文件添加了內聯樣式。
    html
    <p
      style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;">
      We hope you enjoyed the ride.
    </p>
    

注意:請注意,CSS 樣式的 JavaScript 屬性版本如何使用 小駝峰命名法 編寫,而 CSS 版本則使用連字元 (短橫線命名法)(例如 backgroundColorbackground-color)。確保不要將它們混淆,否則將無法正常工作。

還有另一種常見的動態操作文件樣式的方法,我們現在將介紹。

  1. 刪除您之前新增到 JavaScript 的五行程式碼。
  2. 在您的 HTML <head> 中新增以下內容
    html
    <style>
      .highlight {
        color: white;
        background-color: black;
        padding: 10px;
        width: 250px;
        text-align: center;
      }
    </style>
    
  3. 現在我們將轉向一個非常有用的通用 HTML 操作方法——Element.setAttribute()——它接受兩個引數,即您想要在元素上設定的屬性和您想要設定的值。在本例中,我們將為我們的段落設定一個名為 highlight 的類名
    js
    para.setAttribute("class", "highlight");
    
  4. 重新整理頁面,您將看到沒有任何變化——CSS 仍然應用於段落,但這次是透過為它指定一個類來實現的,該類被我們的 CSS 規則選中,而不是作為內聯 CSS 樣式。

您可以選擇哪種方法;兩種方法都有各自的優點和缺點。第一種方法設定起來更簡單,適用於簡單的用途,而第二種方法更加純粹(不混合 CSS 和 JavaScript,沒有內聯樣式,這些都被視為不良做法)。當您開始構建更大更復雜的應用程式時,您可能會開始更多地使用第二種方法,但這完全取決於您。

到目前為止,我們還沒有真正做任何有用的事情!使用 JavaScript 建立靜態內容沒有意義——您也可以直接將其寫入 HTML 中而不使用 JavaScript。它比 HTML 更復雜,而且使用 JavaScript 建立內容還存在其他問題(例如搜尋引擎無法讀取)。

在下一節中,我們將研究 DOM API 的更實際用途。

注意:您可以在 GitHub 上找到我們 dom-example.html 的完成版本 演示(也可以線上檢視)。

主動學習:動態購物清單

在本挑戰中,我們想製作一個簡單的購物清單示例,允許您使用表單輸入和按鈕動態地向列表新增專案。當您在輸入框中新增專案並按下按鈕時

  • 該專案應該出現在列表中。
  • 每個專案都應該有一個按鈕,可以按下該按鈕將該專案從列表中刪除。
  • 輸入框應該被清空並獲得焦點,以便您可以輸入另一個專案。

完成的演示應該看起來像這樣

Demo layout of a shopping list. A 'my shopping list' header followed by 'Enter a new item' with an input field and 'add item' button. The list of already added items is below, each with a corresponding delete button.

要完成練習,請按照以下步驟操作,並確保列表的行為如上所述。

  1. 首先,下載我們 shopping-list.html 起始檔案的副本,並將其複製到某個地方。您會看到它有一些最小的 CSS、一個帶有標籤、輸入框和按鈕的 div、一個空的列表和 <script> 元素。您將在指令碼中進行所有新增操作。
  2. 建立三個變數,它們分別儲存對列表 (<ul>)、<input><button> 元素的引用。
  3. 建立一個 函式,該函式將在按鈕被點選時執行。
  4. 在函式主體內部,首先將輸入元素的當前 儲存在一個變數中。
  5. 接下來,透過將輸入元素的值設定為一個空字串——''——來清空它。
  6. 建立三個新元素——一個列表項 (<li>)、<span><button>,並將它們儲存在變數中。
  7. 將 span 和按鈕作為子元素附加到列表項中。
  8. 將 span 的文字內容設定為之前儲存的輸入元素的值,並將按鈕的文字內容設定為“刪除”。
  9. 將列表項作為子元素附加到列表中。
  10. 向刪除按鈕附加一個事件處理程式,以便在點選時,它將刪除整個列表項 (<li>...</li>)。
  11. 最後,使用 focus() 方法使輸入元素獲得焦點,以便輸入下一個購物清單專案。

注意:如果您真的卡住了,請檢視我們 完成的購物清單也可以線上檢視)。

總結

我們已經完成了對文件和 DOM 操作的研究。此時,您應該瞭解 Web 瀏覽器在控制文件和使用者的 Web 體驗的其他方面時所涉及的重要部分。最重要的是,您應該瞭解什麼是文件物件模型以及如何操作它以建立有用的功能。

另請參閱

您可以使用更多功能來操作文件。檢視我們的一些參考資料,看看您能發現什麼。

(檢視我們的 Web API 索引,以獲取 MDN 上記錄的完整 Web API 列表!)