Back
PQCHub

前端互動基礎 – HTML 架構與 DOM 互動

對於習慣寫 Node.js 的你來說,可以這樣理解:HTML 是瀏覽器讀取的資料結構,它定義了介面的「形狀」。


1. 網頁的骨架 – HTML 基礎架構

HTML (HyperText Markup Language) 並不是程式語言,它是一種「標記語言」。如果把網頁想像成一份報紙,HTML 就是用來定義哪裡是標題、哪裡是內文、哪裡是圖片的排版指令。

對於習慣寫 Node.js 的你來說,可以這樣理解:HTML 是瀏覽器讀取的資料結構,它定義了介面的「形狀」。

1.2 HTML 的樹狀結構 (The Tree Structure)

HTML 的核心概念是「巢狀 (Nested)」與「樹狀 (Tree)」。

  • 標籤 (Tags):HTML 由 <標籤名> 開始,並由 </標籤名> 結束。
  • 父子關係:包在裡面的稱為「子元素 (Child)」,外面的稱為「父元素 (Parent)」。

這種層級關係非常重要,因為之後我們用 JavaScript (DOM) 抓取資料時,往往就是沿著這棵樹在移動。

1.3 標準檔案結構:骨架解析

一個標準的 HTML5 檔案結構非常固定,主要分為三個層次:宣告、頭部 (Head)、身體 (Body)。

完整範例 (index.html)

<!DOCTYPE html>
<html>
  <head>
      <meta charset="UTF-8">
      <title>我的資料分析儀表板</title>
  </head>

  <body>
      <h1>歡迎來到資料科學課程</h1>
      <p>這是一個段落文字。</p>
  </body>
</html>

1.4 逐行詳細解說

我們將上述代碼拆解來看,每一行都有其存在的意義:

A. 宣告身分

<!DOCTYPE html>
  • 意義:這不是 HTML 標籤,這是一行「聲明」。
  • 作用:它告訴瀏覽器:「請使用最新的 HTML5 標準 來解析這份文件」。如果不寫這行,瀏覽器可能會進入「怪異模式 (Quirks Mode)」,導致排版亂掉。

B. 根元素 (The Root)

<html> ... </html>
  • 意義:這是整棵樹的「根 (Root)」,所有內容都要包在這裡面。
  • lang="zh-TW":這是一個屬性,告訴瀏覽器「這個網頁的主要語言是繁體中文」。這對於瀏覽器的翻譯功能和搜尋引擎很重要。

C. 頭部:設定區 (The Head)

<head> ... </head>
  • 意義:這是網頁的「後台設定」或「腦袋」。
  • 特點:寫在這裡的東西絕大部分不會直接顯示在網頁畫面上。
  • 關鍵內容
    1. <meta charset="UTF-8">非常重要! 這告訴瀏覽器使用 UTF-8 編碼。如果你忘了寫這行,你的中文內容很可能會變成亂碼(尤其是在 Windows 系統上)。
    2. <title>...</title>:這是顯示在瀏覽器「分頁標籤」上的文字,也是加入我的最愛時的預設名稱。

D. 身體:顯示區 (The Body)

<body> ... </body>
  • 意義:這是網頁的「舞台」或「軀幹」。
  • 特點:所有使用者看得到的內容(按鈕、圖片、文字、表格)都必須放在這裡面。
  • 注意:我們之後寫的 JavaScript <script> 標籤,通常也會放在 <body> 的最下方(在 </body> 結束之前),確保網頁內容先跑出來,再執行程式邏輯。

1.5 小結:VS Code 快速指令

如果你使用 VS Code 開發,不需要背誦上述結構。 只要在空的 HTML 檔案中輸入 ! (驚嘆號) 然後按下 Tab 鍵,VS Code 就會自動幫你生成這整套標準骨架。


2. 關鍵元件介紹 – 打造互動介面的積木

在 HTML 的世界裡有上百種標籤,但對於資料科學的互動應用來說,我們不需要花俏的排版,只需要掌握三個核心功能的元件:分組容器資料輸入觸發按鈕

把它們想像成樂高積木,我們將用這三種積木堆疊出使用者的操作介面。

2.1 預備知識:什麼是屬性 (Attributes)?

在介紹元件之前,要先理解「屬性」。HTML 標籤通常長這樣: <標籤名 屬性="值">

  • 標籤名:定義它是什麼(例如:這是一個按鈕)。
  • 屬性:定義它的特性(例如:這個按鈕是紅色的、這個輸入框只能填數字)。

2.2 分組容器:<div> (Division)

  • 角色收納盒
  • 用途<div> 本身沒有任何外觀或功能,它唯一的用途就是「把相關的東西包在一起」。
  • 排版特性 (Block Element)
    • <div> 預設是「區塊元素」,意思就是它很霸道,會獨佔一整行
    • 當你寫了兩個 <div>,第二個會自動換行跑到下面去。這讓我們可以很輕鬆地把介面切分成「上、中、下」區塊。

應用場景: 我們通常會用一個 <div> 包住「輸入區」,用另一個 <div> 包住「結果顯示區」,讓介面井然有序,不會全部擠在同一行。

<div>
    <h3>輸入區</h3>
    ...
</div>

<div>
    <h3>結果區</h3>
    ...
</div>

2.3 資料輸入:<input>

  • 角色數據接口
  • 用途:這是使用者與程式溝通的窗口。
  • 語法特性:它是「自我閉合 (Self-closing)」標籤,意思是它沒有結束標籤,寫法是 <input ... >

關鍵屬性:

  1. type (類型):最重要的屬性,決定輸入框長什麼樣子。
    • type="text":預設值。適合輸入姓名、備註等文字。
    • type="number"資料科學最常用! 瀏覽器會限制只能輸入數字,且手機版會自動彈出數字鍵盤。
  2. placeholder (提示文字):當輸入框是空的時候,顯示灰色的提示字(例如:「請輸入體重...」),使用者一打字就會消失。
<input type="text" placeholder="請輸入姓名">

<input type="number" placeholder="請輸入數值">

2.4 觸發按鈕:<button>

  • 角色板機 / 開關
  • 用途:告訴程式「資料填好了,請開始運算」。在我們寫 JavaScript 之前,點它不會有反應,但它是觸發所有邏輯的起點。
  • 語法特性:成對標籤,中間夾的文字就是按鈕上顯示的字。
<button>開始分析</button>
<button>清除重填</button>

2.5 靈魂屬性:id (身分證字號)

這是這一章節最重要的概念。

HTML 頁面上可能會有 5 個 <input> 和 3 個 <button>,當我們寫 JavaScript 指令說「取得輸入框的值」時,電腦會問:「哪一個輸入框?」

為了精準控制,我們必須給關鍵元素一個獨一無二的代號,這就是 id

  • 規則 1:唯一性。同一個頁面裡,不能有兩個元素叫同一個 id(就像一個班級不能有兩個座號 1 號)。
  • 規則 2:命名規範。建議使用英文,不要用空格,單字間可用連字號 - 或底線 _ 分隔(例如 user-agebtn_submit)。

錯誤示範 vs 正確示範

錯誤 (JavaScript 會抓錯人):

<input type="number">
<input type="number"> 

正確 (JavaScript 可以精準抓取):

<input type="number" id="input-height">
<input type="number" id="input-weight">
<button id="btn-calc">計算</button>

3 組合 – 實作你的第一個互動網頁

現在我們已經認識了所有的積木(HTML 架構、Input、Button、Div),接下來我們要當建築師,把它們組裝起來。

為了讓大家最快上手,我們採用 onclick (點擊事件) 的寫法。這種寫法就像是直接在按鈕上貼一張便利貼,告訴它:「被按的時候,請執行這個動作」。

3.1 建立檔案

請在你的專案資料夾中,建立一個檔案 index.html

3.2 完整程式碼實作

請將以下代碼完全複製進去。請注意 <button> 標籤裡的 onclick 屬性,以及底下的 <script> 內容。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>數值判斷器 v1.0</title>
</head>
<body>

    <div id="input-section">
        <h3>步驟 1:輸入數據</h3>
        <p>請輸入一個數值,系統將判斷其大小:</p>
        
        <input type="number" id="data-input" placeholder="例如:100">
        
        <button onclick="runAnalysis()">送出分析</button>
    </div>

    <hr> <div id="output-section">
        <h3>步驟 2:分析結果</h3>
        
        <div id="result-area" style="color: gray; font-weight: bold;">
            (等待輸入中...)
        </div>
    </div>

    <script>
        // --- JavaScript 邏輯區 ---

        // 定義一個函式 (Function),名稱必須跟上面 onclick 設定的一模一樣
        function runAnalysis() {
            
            // 1. [抓取元素] 只要抓輸入框(input)和顯示區(div)就好
            // (因為按鈕已經用 onclick 綁定了,不需要特別抓它)
            const inputEl = document.getElementById('data-input');
            const resultEl = document.getElementById('result-area');

            // 2. [讀取數值] x + y 的縮寫。
它的意思是:「把 listArea 原本的 HTML 內容拿出來,在後
            const rawVal = inputEl.value;         // 拿到的是字串
            const numVal = Number(rawVal);        // 轉成數字

            // 3. [運算與判斷]
            let message = "";
            let textColor = "black";

            if (rawVal === "") {
                message = "⚠️ 警告:你沒有輸入任何數字!";
                textColor = "red";
            } else if (numVal > 50) {
                message = `📈 數據分析:數值 ${numVal} 大於 50 (高標)`;
                textColor = "green";
            } else {
                message = `📉 數據分析:數值 ${numVal} 小於或等於 50 (低標)`;
                textColor = "blue";
            }

            // 4. [輸出結果] 把文字塞回 HTML
            resultEl.textContent = message;
            resultEl.style.color = textColor;
        }

    </script>

</body>
</html>

3.4 令一種寫法,用JS綁定點擊按鈕後觸發的方法

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>數值判斷器 v1.0</title>
</head>
<body>

    <div id="input-section">
        <h3>步驟 1:輸入數據</h3>
        <p>請輸入一個數值,系統將判斷其大小:</p>
        
        <input type="number" id="data-input" placeholder="例如:100">
        <!-- 注意這裡不同! -->
        <button id="submit">送出分析</button>
    </div>

    <hr> <div id="output-section">
        <h3>步驟 2:分析結果</h3>
        
        <div id="result-area" style="color: gray; font-weight: bold;">
            (等待輸入中...)
        </div>
    </div>

    <script>
        // --- JavaScript 邏輯區 ---

        // 定義一個函式 (Function),名稱必須跟上面 onclick 設定的一模一樣
        function runAnalysis() {
            
            // 1. [抓取元素] 只要抓輸入框(input)和顯示區(div)就好
            // (因為按鈕已經用 onclick 綁定了,不需要特別抓它)
            const inputEl = document.getElementById('data-input');
            const resultEl = document.getElementById('result-area');

            // 2. [讀取數值]
            const rawVal = inputEl.value;         // 拿到的是字串
            const numVal = Number(rawVal);        // 轉成數字

            // 3. [運算與判斷]
            let message = "";
            let textColor = "black";

            if (rawVal === "") {
                message = "⚠️ 警告:你沒有輸入任何數字!";
                textColor = "red";
            } else if (numVal > 50) {
                message = `📈 數據分析:數值 ${numVal} 大於 50 (高標)`;
                textColor = "green";
            } else {
                message = `📉 數據分析:數值 ${numVal} 小於或等於 50 (低標)`;
                textColor = "blue";
            }

            // 4. [輸出結果] 把文字塞回 HTML
            resultEl.textContent = message;
            resultEl.style.color = textColor;
        }
        
        // 注意這裡不同!
        let submitEl = document.getElementById('submit');
        submitEl.onclick = runAnalysis;

    </script>

</body>
</html>

4 資料流的處理 – 綜合實作

在 Part 3,我們成功讓網頁「動」了起來。但在處理數據時,我們必須注意資料型別 (Data Types) 的問題,並學習如何用 JavaScript 產生更豐富的結果。

這章節我們將完成一個 BMI 健康分析器

4.1 新手的惡夢:字串陷阱 (The String Trap)

這是所有學 JavaScript 的人一定會遇到的坑。

當我們使用 document.getElementById('...').value 從 HTML 的 <input> 拿資料時,不管使用者輸入的是不是數字,瀏覽器預設通通都會把它當成「字串 (String)」

恐怖故事:10 + 10 = ?

如果你寫了這樣的程式碼:

let a = inputA.value; // 假設使用者輸入 10
let b = inputB.value; // 假設使用者輸入 10

let result = a + b;

在 Node.js 處理純數字時你會得到 20,但在這裡,你會得到 "1010"! 因為對電腦來說,這是文字的串接,不是數學的加法。

解法:強制轉型

在進行任何運算之前,務必使用 Number() 包裹取回來的值。

// ✅ 正確做法:先轉成數字
let h = Number(inputHeight.value); 
let w = Number(inputWeight.value);

4.2 綜合實作:BMI 健康分析器 (Mini Project)

讓我們運用目前為止學到的:HTML 架構onclick 事件資料轉型,來完成工具。

專案需求

  1. 輸入:身高(公分)、體重(公斤)。
  2. 邏輯
    • 將身高換算成公尺(除以 100)。
    • 計算 BMI = kg / (m * m)
    • 取小數點後兩位 (.toFixed(2))。
  3. 輸出:顯示 BMI 數值與健康判定。

完整程式碼 (index.html)

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>BMI 健康分析器</title>
</head>
<body>

    <div>
        <h3>BMI 健康計算器</h3>
        
        <p>身高 (cm):</p>
        <input type="number" id="height-input" placeholder="例如:175">
        
        <p>體重 (kg):</p>
        <input type="number" id="weight-input" placeholder="例如:70">
        
        <br><br>
        
        <button onclick="calcBMI()">開始計算</button>
    </div>

    <hr>

    <div>
        <h4>分析報告:</h4>
        <div id="result-area">尚未計算</div>
    </div>

    <script>
        function calcBMI() {
            // --- A. 抓取與轉型 (Get Data) ---
            // 記得!從 input 拿出來的一定要是 Number
            const h_input = document.getElementById('height-input').value;
            const w_input = document.getElementById('weight-input').value;

            // 簡單的防呆:檢查有沒有輸入
            if (h_input == "" || w_input == "") {
                alert("請輸入完整身高與體重!");
                return; // 結束函式,不往下執行
            }

            const heightCm = Number(h_input);
            const weightKg = Number(w_input);

            // --- B. 運算邏輯 (Process Data) ---
            // 1. 公分轉公尺
            const heightM = heightCm / 100;
            
            // 2. 套用公式
            const bmiValue = weightKg / (heightM * heightM);
            
            // 3. 取小數點後兩位 (這會變回字串,適合顯示用)
            const finalBMI = bmiValue.toFixed(2);

            // 4. 判斷體位
            let status = "";

            if (bmiValue < 18.5) {
                status = "體重過輕";
            } else if (bmiValue >= 18.5 && bmiValue < 24) {
                status = "健康體位";
            } else {
                status = "體重過重";
            }

            // --- C. 顯示結果 (Output Data) ---
            // 我們使用 innerHTML,這樣可以塞入 <b> 標籤讓重點加粗
            const resultDiv = document.getElementById('result-area');
            
            // 使用樣板字串 (Template Literals) 組合 HTML
            resultDiv.innerHTML = `
                您的 BMI 指數為:<b>${finalBMI}</b> <br>
                健康判定:<b>${status}</b>
            `;
        }
    </script>

</body>
</html>

4.3 程式碼重點解析

  1. alert(...):這是一個很方便的除錯小工具,會跳出一個瀏覽器視窗警告使用者。
  2. .toFixed(2):資料科學常會算出 24.123123123 這種無限小數,用這個方法可以只保留兩位小數,讓介面更乾淨。
  3. innerHTML vs textContent
    • 如果你只用 .textContent,結果出來會是單純的文字。
    • 我們這裡用 .innerHTML,是為了能使用 <b>...</b> (粗體) 標籤,讓使用者一眼看到重點數字。

5: 讓數據留下痕跡 – 列表與輸入重置

目前的 BMI 計算器是一個「一次性」的工具。這章節我們要升級它,讓它具備兩個優化功能:

  1. 歷史紀錄:把每次算出來的 BMI 往下條列,而不是覆蓋。
  2. 自動清空:按鈕送出後,自動把輸入框清空,方便輸入下一筆。

5.1 覆蓋 vs. 累加 (Overwrite vs. Append)

這是程式邏輯上的小改變,但對使用者體驗影響巨大。

  • 覆蓋 (=)resultDiv.innerHTML = "新的結果" 舊的內容直接被丟掉。

  • 累加 (+=)resultDiv.innerHTML += "新的結果" 原本的內容保留,新的內容接在屁股後面。

5.2 實作:BMI 歷史紀錄器

我們不用修改太複雜的 HTML,只需要多準備一個「容器」來放歷史資料。

完整程式碼 (index.html)

請將這段代碼複製到你的檔案中。

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>BMI 歷史紀錄器</title>
</head>
<body>

    <div>
        <h3>BMI 數據登錄</h3>
        
        <p>身高 (cm):</p>
        <input type="number" id="height-input" placeholder="例如:175">
        
        <p>體重 (kg):</p>
        <input type="number" id="weight-input" placeholder="例如:70">
        
        <br><br>
        
        <button onclick="addData()">加入紀錄</button>
    </div>

    <hr>

    <div>
        <h4>數據紀錄表:</h4>
        
        <ul id="history-list">
            </ul>
    </div>

    <script>
        function addData() {
            // --- A. 抓取資料 ---
            const hInput = document.getElementById('height-input');
            const wInput = document.getElementById('weight-input');

            // 取得數值
            const hVal = Number(hInput.value);
            const wVal = Number(wInput.value);

            // 防呆檢查
            if (hInput.value == "" || wInput.value == "") {
                alert("請輸入完整數值!");
                return;
            }

            // --- B. 運算邏輯 ---
            const heightM = hVal / 100;
            const bmi = (wVal / (heightM * heightM)).toFixed(2);

            // --- C. 更新歷史紀錄 (Append Data) ---
            const listArea = document.getElementById('history-list');

            // 這裡我們使用 += (累加)
            // <li> 是列表項目標籤
            // 我們把運算結果包成一個 HTML 字串,接在原本內容的後面
            listArea.innerHTML += `
                <li>
                    身高: ${hVal}cm / 體重: ${wVal}kg 
                    => <b>BMI: ${bmi}</b>
                </li>
            `;

            // --- D. 優化體驗:清空輸入框 ---
            // 讓使用者不用手動刪除舊數字,可以直接打下一筆
            hInput.value = "";
            wInput.value = "";
        }
    </script>

</body>
</html>

5.3 程式碼細節解析

A. 清單標籤 <ul><li>

這是 HTML 專門用來做列表的標籤。

  • <ul> (Unordered List):列表的外框
  • <li> (List Item):列表的每一項
  • 瀏覽器預設會幫 <li> 前面加上一個小黑點(Bullet point),這非常適合用來顯示 Data Log。

B. 魔法運算符 +=

注意看這行:

listArea.innerHTML += `<li>...</li>`;

這其實是 x = x + y 的縮寫。 它的意思是:「把 listArea 原本的 HTML 內容拿出來,在後面黏上新的 <li> 字串,然後再塞回去。」

這就是最簡單的動態新增元素方法。

C. 清空輸入框 (Reset)

hInput.value = "";

這行看似簡單,卻是 UX(使用者體驗)的關鍵。 在資料科學的資料輸入介面(Data Entry)中,使用者通常會連續輸入多筆資料。如果每次送出後,輸入框還留著上一次的數字,使用者就必須一直按 Backspace 刪除,非常惱人。透過程式自動清空,可以讓輸入流程變得很順暢。