回到基礎:如何用原生 DOM API 生成表格

前端先鋒發表於2019-03-13

翻譯:瘋狂的技術宅

原文:https://www.valentinog.com/blog/html-table/

怎樣用原生 JavaScript 生成表格需?本文告訴你答案!

回到基礎:如何用原生 DOM API 生成表格

這是一個刷 JavaScript 經驗值的好機會:在技術面試中出現的最多的一個問題就是**怎樣用原生 API 操作 DOM **。

在下面的教程中,我們將瞭解如何使用 JavaScript 生成表格,而無需依賴任何庫或框架。

你將學到些什麼

在本教程中,你將學習如何:

  • 用 JavaScript 生成一個表格
  • 用本機 DOM API 來操作表

要求

要學習本教程,你應該對 HTML 和 JavaScript 有基本的瞭解。

需求

Eloquent JavaScript 是一本非常好的 JavaScript 書籍。這本書很簡潔,也不乏味,同時有大量的練習。以下練習改編自第 14 章,它被稱為“構建表格”。

題目要求你用 JavaScript 構建一個 HTML 表。你的任務是依據 “mountains” 陣列中的資料生成表格,將物件中的key對應到列並且每行一個物件

每個物件都是如下形式:

{ name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" }
複製程式碼

我們有一個名稱,一個高度和一個山峰所在的位置。但 HTML 表格是什麼? HTML 表格是包含表格資料的元素,以行和列的形式顯示。這意味著給出以下陣列:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" }
];
複製程式碼

我們打算生成下表:

<table>
    <thead>
    <tr>
        <th>name</th>
        <th>height</th>
        <th>place</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Monte Falco</td>
        <td>1658</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    <tr>
        <td>Monte Falterona</td>
        <td>1654</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    </tbody>
</table>
複製程式碼

如你所見,該表有一個 thead(表頭),其中包含一個具有三個**th(表格標題)**的 **tr(表格行) **。

然後是tbody(表體) 中包含一堆 tr(表格行)。每個表格行包含一定數量的 td元素(表格單元格)

有了這些要求,就可以開始編寫 JavaScript 檔案了。我們的起點可以是以下 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Build a table</title>
</head>
<body>
<table>
<!-- here goes our data! -->
</table>
</body>
<script src="build-table.js"></script>
</html>
複製程式碼

將檔案另存為 **build-table.html ** 並繼續下一部分。

生成表頭

在與 build-table.html 相同的資料夾中建立一個名為 build-table.js 的新檔案,並在檔案定義陣列:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];
複製程式碼

第一個目標是生成表頭。我們知道本機方法 createElement() 會建立傳遞給它的任何元素。假設我們要建立一個表頭,可以用 document.createElement(‘thead’)。不過還有更好的辦法嗎?

讓我們先到 MDN 上查閱一下 element table reference 。你可以看到表格的DOM介面是 HTMLTableElement

HTMLTableElement 的有趣之處在於它公開的方法中有 createTHead()。沒錯!createTHead 返回與給定表關聯的表頭元素,更 6 的是,如果表中不存在頭的話,createTHead 會幫我們建立一個。

有了這些知識,接下來在我們的檔案中建立一個函式,將 table 作為引數。鑑於我們的需求,可以在其中建立一個新的 thead

function generateTableHead(table) {
  let thead = table.createTHead();
}
複製程式碼

現在找到我們的表格(記住在 build-table.html 中有一個)並將其傳給我們的函式:

function generateTableHead(table) {
  let thead = table.createTHead();
}

let table = document.querySelector("table");
generateTableHead(table);
複製程式碼

如果你在瀏覽器中開啟 build-table.html,在螢幕上還看不到任何內容,不過可以在開發者工具中看到處理的結果。填充表頭的工作只做了一半,可以看到表頭中填充了一堆 th。每個表頭必須對映到物件描述資料組成的 key 上。

資訊已經存在於陣列 mountains 中的第一個物件內部。可以迭代第一個物件的 key:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
    //
];
複製程式碼

然後用得到的 key 生成三個表頭。但是要先在 thead 中新增一行!這時候該用 document.createElement(“TR”) 了吧?不不不。HTMLTableRowElement 提供了一個 insertRow() 方法,可以在表頭上呼叫。讓我們重構一下 generateTableHead 函式:

function generateTableHead(table) {
  let thead = table.createTHead();
  let row = thead.insertRow();
}
複製程式碼

新行應包含三個 th(表頭),需要手動建立,對於每個 th 元素,我們將附加一個文字節點。這個函式可以使用另一個引數來進行迭代:

function generateTableHead(table, data) {
  let thead = table.createTHead();
  let row = thead.insertRow();
  for (let key of data) {
    let th = document.createElement("th");
    let text = document.createTextNode(key);
    th.appendChild(text);
    row.appendChild(th);
  }
}

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTableHead(table, data);
複製程式碼

儲存檔案並重新整理 build-table.html:你應該看到你的表頭中被填充了 name、height 和 place。 有時用 React 和 Vue 偷懶的感覺真好,直接操作 DOM 是多麼艱難和繁瑣。不過我們的工作還沒有完成。

接下來該填表了......

生成行和單元格

為了填充表格可以遵循同樣的方法,但這次我們需要迭代 mountains 陣列中的每個物件。當進入 for…of 迴圈時,將為每個專案建立一個新行

要建立行,你將用到 insertRow()

但我們不能止步於此。在主迴圈內部,需要一個內迴圈,這次要用到 **for... in **。內部迴圈迭代當前物件的每個 key,同時它:

  • 建立一個新單元格
  • 建立一個新的文字節點
  • 將文字節點附加到單元格

使用 HTMLTableRowElement 的另一個方法 insertCell() 建立單元格。

也就是說通過以上邏輯可以填充我們的表。開啟 build-table.js 並建立一個名為 generateTable 的新函式。命名可以與我們現有的函式相同:

function generateTable(table, data) {
  for (let element of data) {
    let row = table.insertRow();
    for (key in element) {
      let cell = row.insertCell();
      let text = document.createTextNode(element[key]);
      cell.appendChild(text);
    }
  }
}
複製程式碼

可以這樣呼叫它:

generateTable(table, mountains);
複製程式碼

最後來看看完整的程式碼:

let mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];

function generateTableHead(table, data) {
  let thead = table.createTHead();
  let row = thead.insertRow();
  for (let key of data) {
    let th = document.createElement("th");
    let text = document.createTextNode(key);
    th.appendChild(text);
    row.appendChild(th);
  }
}

function generateTable(table, data) {
  for (let element of data) {
    let row = table.insertRow();
    for (key in element) {
      let cell = row.insertCell();
      let text = document.createTextNode(element[key]);
      cell.appendChild(text);
    }
  }
}

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTableHead(table, data);
generateTable(table, mountains);
複製程式碼

你覺得它能工作嗎?讓我們來試試看:

生成行和單元格

呃……看起來行被附加到了表頭而不是表體。另外沒有table body

但是如果切換函式呼叫順序會怎麼樣呢?試試吧:

// omitted for brevity

let table = document.querySelector("table");
let data = Object.keys(mountains[0]);
generateTable(table, mountains); // generate the table first
generateTableHead(table, data); // then the head
複製程式碼

再次重新整理瀏覽器:

生成行和單元格,插入行

好使!另外還得到了一個 tbody。為什麼會這樣?當你在空表上呼叫 insertRow() 時,這些方法會為自動你建立一個tbody(如果沒有的話)。

做得好!不過我們的程式碼可能沒進行很好的組織(有太多的全域性繫結),這些將會在下一篇文章中提到。

到此為止,你應該能夠在不依賴任何外部庫的情況下操作HTML表了。恭喜!

總結

在本教程中,我們學到了如何用原生 JavaScript 生成表格。 HTML 表格在DOM中由 HTMLTableElement 體現。這個介面公開了很多有用的方法,其中 createTHead 用於操作表頭,insertRow 用來插入行。

另外 HTML 表格的行繼承自 HTMLTableRowElement。這個介面有兩種方法,其中最重要的是 insertCell

給定一個物件陣列,可以使用 for…of 迴圈來迭代生成行。對於每個物件,我們可以使用 for … in 生成單元格。

我們有一些帶有全域性繫結的程式碼(請參閱執行上下文和呼叫堆疊以獲取更多資訊)。在下一篇文章中,我們將看到怎樣重構這些程式碼。

jQuery正逐漸消失。 Bootstrap將在版本5中刪除它。 **原生DOM API **越來越好了,替換以前用 jQuery 做的事情是可行的,沒有(幾乎)任何額外的依賴。

但即使沒有 jQuery 也很容易掉進坑裡。有人說你不應該在沒有 $shinyNewLibrary 的情況下去操縱 DOM。實際上**每個認真的 JavaScript 開發人員都應該知道原生 DOM API,以及如何使用 JavaScript 操作 DOM **。這些問題在技術面試中很容易被問到,你不想因此被拒絕吧?

本教程的程式碼可在 Github 下載(https://github.com/valentinogagliardi/back-to-js-basics)。

感謝閱讀並敬請期待後續!

歡迎關注京程一燈公眾號:jingchengyideng,獲取更多前端乾貨。

相關文章