STEP GUIDE · Checkpoint 2

這個步驟要做什麼、改哪裡、怎麼看結果

這一步聚焦 TASK-02;下方只比較「Checkpoint 1 → Checkpoint 2」的真實異動。

01

三項 Task 進度

TASK-01已完成

建立 mock 或 GAS 請求函式

預計修改:script.js

完成後:頁面能取得固定欄位的回應

TASK-02本步驟處理

正規化並安全顯示 Sheet rows

預計修改:script.js

完成後:空值與 Live 文字都以安全文字節點顯示

TASK-03稍後處理

顯示資料模式與失敗備援

預計修改:script.js

完成後:畫面區分 mock、空資料、合法 GAS /exec 與其他 JSON,live 失敗時可改用備援

02

HTML、CSS、JavaScript 與文件實際異動

展開檔案卡查看必要差異;只有程式碼區可以橫向捲動。

HTML本步驟無異動

檔案:index.html

修改前

前一步與本步驟沒有實質差異。

修改後

本步驟不需修改此語言。
CSS本步驟無異動

檔案:style.css

修改前

前一步與本步驟沒有實質差異。

修改後

本步驟不需修改此語言。
JavaScript本步驟有異動

檔案:script.js

修改前

  return rows;
function renderRows() {
  rowsElement.replaceChildren();

修改後

  return rows.map((row, index) => ({
    id: row.id ?? index + 1,
    name: String(row.name || "未命名"),
    price: Number(row.price) || 0,
  }));
function createRowCard(row) {
  const card = document.createElement("article");
  card.className = "item-card";
  const title = document.createElement("h2");
  title.textContent = row.name;
  const price = document.createElement("p");
  price.textContent = `$${row.price}`;
  card.append(title, price);
  return card;
function renderRows(rows) {
  if (rows.length === 0) {
    const emptyMessage = document.createElement("p");
    emptyMessage.className = "empty-state";
    emptyMessage.textContent = "目前沒有試算表資料。";
    rowsElement.replaceChildren(emptyMessage);
    return;
  }
  rowsElement.replaceChildren(...rows.map(createRowCard));
}
README/其他文件本練習未使用

檔案:README.md

修改前

本練習沒有這個檔案。

修改後

本步驟不需修改此語言。
03

怎麼看到結果

  1. 準備

    先以 mock 模式操作;測 live 前再準備可用的 GAS 網址。

  2. 操作

    在 Console 執行 renderRows(normalizeRows([{ id: 3, name: '<img src=x onerror=alert(1)>', price: '' }])),確認畫面顯示原始文字、價格 0,且沒有產生 img 或跳出視窗。

  3. 預期

    空值與 Live 文字都以安全文字節點顯示

Checkpoint 2 TASK-02

正規化並安全顯示 Sheet rows

完成後可見:空值與 Live 文字都以安全文字節點顯示

MOCK 備援

Sheet 產品資料

輸入 mock、mock-empty,或教師提供的 HTTPS GAS /exec URL;其他網址不會標成 LIVE GAS。

尚未載入