這一課完成後:你會知道 HTML 和 DOM 不是完全同一件事,能使用 querySelector / querySelectorAll 找到元素,修改文字、class、attribute,並從 JavaScript 資料建立新的 elements。

先把 JavaScript 正式接進頁面

沿用前幾課的 web-basics-playground,確認根目錄至少有:

web-basics-playground/
├─ index.html
├─ styles.css
└─ script.js

在 index.html 的 <head> 裡加入:

<script src="script.js" defer></script>

src 指向 JavaScript 檔案;defer 則告訴瀏覽器:先繼續解析 HTML,等文件結構準備好之後再執行這支 script。

這可以避免初學者很常遇到的問題:JavaScript 執行時,想找的 HTML 元素其實還沒被瀏覽器建立。

HTML 文件進到瀏覽器後,不只是原始文字

假設 HTML 有:

<main>
  <h1>Web Basics</h1>
  <p class="intro">我正在學 DOM。</p>
</main>

瀏覽器解析後,會建立類似樹狀結構:

document
└─ html
   └─ body
      └─ main
         ├─ h1
         └─ p.intro

這個可由程式操作的文件模型,就是 DOM — Document Object Model。

HTML 是來源文件;DOM 是瀏覽器解析後建立、目前正在記憶體中存在的文件結構。JavaScript 操作的主要是 DOM。

document 是你進入 DOM 的入口

打開瀏覽器 DevTools Console,輸入:

document

你會看到目前頁面的 document object。

也可以試:

document.title

它會讀出目前頁面的 <title>。

再試:

document.title = "DOM Practice";

瀏覽器分頁標題會立刻改變。這就是第一次直接用 JavaScript 修改目前 DOM 狀態。

重新整理後,如果原始 HTML 沒改,分頁標題通常會恢復。因為你剛才改的是瀏覽器目前記憶體中的 DOM,不是硬碟上的 index.html。

先找到一個元素:querySelector

在 HTML 放一個:

<h1 id="page-title">我的第一個網站</h1>
<p class="intro">這段內容等等會被 JavaScript 找到。</p>

在 script.js:

const title = document.querySelector("#page-title");
const intro = document.querySelector(".intro");

console.log(title);
console.log(intro);

querySelector() 使用的就是你 CSS 已經看過的 selector 語法。

#page-title → id selector
.intro → class selector
h1 → element selector

它只會回傳第一個符合條件的元素;如果完全找不到,結果會是 null。

先確認你真的找到東西,再往下改

這種程式:

const message = document.querySelector(".message");
console.log(message);

如果 Console 印出 null,通常代表 selector 寫錯、HTML 沒有這個元素,或 script 執行時機不對。

不要看到 Cannot set properties of null 就亂改 JavaScript。先確認「我要操作的 element 到底有沒有被選到」。

讀取與修改文字:textContent

const title = document.querySelector("#page-title");

console.log(title.textContent);
title.textContent = "JavaScript 已經找到這個標題";

textContent 可以讀取或設定元素內的文字內容。

如果只是要放純文字,優先使用它。它不會把字串當成 HTML 解析,因此對來自使用者或 API 的文字也比較安全。

innerHTML 可以放 HTML,但不要把它當預設選項

你可能會看到:

element.innerHTML = "<strong>Hello</strong>";

這會把字串解析成 HTML。它確實方便,但如果內容裡混入不可信資料,就可能造成安全問題。

純文字 → 優先 textContent
要建立 DOM 結構 → 優先 createElement
innerHTML → 只有你清楚知道內容來源與風險時再用

這門課不會把 innerHTML 當成「快速把所有東西塞進頁面」的方法。

修改 CSS class:classList

先在 styles.css 加:

.highlight {
  background: #fff4bf;
  border-radius: 12px;
  padding: 12px;
}

JavaScript:

const intro = document.querySelector(".intro");

intro.classList.add("highlight");

也可以:

intro.classList.remove("highlight");
intro.classList.toggle("highlight");
intro.classList.contains("highlight");

這比直接在 JavaScript 裡一條一條寫 CSS property 更容易維護。

JavaScript 決定狀態 → class 改變 → CSS 決定長相

Attribute 也可以被 JavaScript 操作

HTML:

<a id="project-link" href="https://example.com">專案連結</a>

JavaScript:

const link = document.querySelector("#project-link");

console.log(link.getAttribute("href"));
link.setAttribute("href", "https://github.com/");
link.setAttribute("target", "_blank");

很多常見 attribute 也可以直接透過 property 操作,例如:

link.href = "https://github.com/";

現在不用背「哪個一定要用哪種」。先知道 DOM element 是 object,而它的 attributes / properties 都可以被程式讀寫。

一次找多個元素:querySelectorAll

假設 HTML:

<ul>
  <li class="skill">HTML</li>
  <li class="skill">CSS</li>
  <li class="skill">JavaScript</li>
</ul>

JavaScript:

const skills = document.querySelectorAll(".skill");

console.log(skills);

querySelectorAll() 會回傳一組符合的節點,常見型態是 NodeList。

你可以直接:

for (const skill of skills) {
  console.log(skill.textContent);
}

上一課學的 loop,現在第一次真的走進頁面。

讓每一個 element 都根據資料改變

const skills = document.querySelectorAll(".skill");

for (const skill of skills) {
  skill.textContent = `正在學:${skill.textContent}`;
}

這就是很典型的前端工作:先選到一組 DOM elements,再依照資料與規則改變它們。

從 JavaScript 建立新的元素

HTML 先放一個容器:

<ul id="lesson-list"></ul>

JavaScript:

const list = document.querySelector("#lesson-list");

const item = document.createElement("li");
item.textContent = "DOM";

list.append(item);

拆開來看:

createElement → 在記憶體建立 element
textContent → 填內容
append → 把它放進 DOM

在 append 之前,這個 li 已經存在於 JavaScript,但還沒有出現在頁面樹上。

把上一課的 Array + Object 真正渲染到頁面

沿用資料:

const lessons = [
  { title: "HTML", done: true },
  { title: "CSS", done: true },
  { title: "JavaScript", done: true },
  { title: "DOM", done: false }
];

再把資料轉成 DOM:

const list = document.querySelector("#lesson-list");

for (const lesson of lessons) {
  const item = document.createElement("li");
  item.textContent = lesson.done
    ? `${lesson.title} ✓`
    : `${lesson.title} — 尚未完成`;

  if (lesson.done) {
    item.classList.add("done");
  }

  list.append(item);
}

這裡已經把前一課的多個概念串起來:

Array/Object → loop → if → createElement → textContent → classList → append

這就是之後前端「拿資料產生畫面」的最小版本。

刪除元素

如果要把一個 element 從 DOM 移除:

const oldMessage = document.querySelector(".old-message");

if (oldMessage) {
  oldMessage.remove();
}

這裡先加 if 是因為 selector 有可能找不到元素。上一課的條件判斷又派上用場。

不要每次都重新 query 同一個元素

如果同一段邏輯會重複操作某個 element,通常先存進變數:

const status = document.querySelector("#status");

status.textContent = "Loading...";
status.classList.add("active");

比這樣清楚:

document.querySelector("#status").textContent = "Loading...";
document.querySelector("#status").classList.add("active");

不只是少打一點字,也是在表達:「後面這幾個動作操作的是同一個 element。」

DOM 修改不等於修改原始 HTML 檔

這件事要再強調一次。

index.html → 瀏覽器解析 → DOM → JavaScript 修改目前 DOM

JavaScript 改了畫面,不代表它拿 VS Code 裡的 index.html 幫你重寫。

重新整理時,瀏覽器會重新讀檔,再重新建立 DOM,然後重新執行 JavaScript。

DevTools 的 Elements 面板現在會非常重要

開啟 DevTools → Elements。你看到的是目前的 DOM,不一定只是原始 HTML 的原封不動複製。

執行 JavaScript 後,如果新增了一個 li、class 被改變、attribute 被替換,你都能在 Elements 面板直接看到。

Console → 看 JavaScript 值與錯誤
Elements → 看目前 DOM 與 CSS

小挑戰:把課程資料真正畫到頁面

不要先做按鈕。這一關只測「資料 → DOM」。

需求 1:HTML 中建立 #course-title、#progress、#lesson-list 三個位置。

需求 2:JavaScript 建立一個 course object,至少有 title 與 lessons。

需求 3:lessons 至少有 4 個 objects,每個有 title、done。

需求 4:使用 querySelector 找到標題與進度位置。

需求 5:根據資料計算完成數,顯示例如「3 / 4 completed」。

需求 6:使用 createElement 與 append 建立每一筆 lesson。

需求 7:完成的 lesson 加上一個 CSS class,不要用 inline style。

需求 8:DevTools Elements 中確認這些 li 真的是 JavaScript 執行後才加入 DOM。

這一課先不要做事件

你現在已經會讓 JavaScript 主動改畫面,但所有動作都是「頁面載入後程式自己執行」。

真正的網站還需要回答另一個問題:

使用者點擊 / 輸入 / 送出 → JavaScript 什麼時候才應該執行?

這就是下一課的 Events。

最後收斂:DOM 是 HTML 和 JavaScript 的橋

HTML → 瀏覽器解析 → DOM
JavaScript → 選取 DOM → 讀取 / 修改 / 建立 / 移除
CSS → 根據 element / class 決定畫面樣式

到這裡,前三種技術第一次真正接在一起了。但目前使用者還沒有控制權;下一課會加入事件,讓程式從「載入就執行」變成「使用者做了某件事才執行」。

完成條件:你能從 JavaScript 選取 DOM elements、讀寫文字與 attributes、使用 classList、走訪多個 elements,並把 array/object 資料透過 createElement 渲染成頁面內容。