DOM 修改是創(chuàng)建“實時”頁面的關(guān)鍵。
在這里,我們將會看到如何“即時”創(chuàng)建新元素并修改現(xiàn)有頁面內(nèi)容。
讓我們使用一個示例進行演示。我們將在頁面上添加一條比 alert
更好看的消息。
它的外觀如下:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>
這是一個 HTML 示例?,F(xiàn)在,讓我們使用 JavaScript 創(chuàng)建一個相同的 div
(假設(shè)樣式已經(jīng)在 HTML/CSS 文件中)。
要創(chuàng)建 DOM 節(jié)點,這里有兩種方法:
?document.createElement(tag)
?
用給定的標簽創(chuàng)建一個新 元素節(jié)點(element node):
let div = document.createElement('div');
?document.createTextNode(text)
?
用給定的文本創(chuàng)建一個 文本節(jié)點:
let textNode = document.createTextNode('Here I am');
大多數(shù)情況下,我們需要為此消息創(chuàng)建像 div
這樣的元素節(jié)點。
創(chuàng)建一個消息 div
分為 3 個步驟:
// 1. 創(chuàng)建 <div> 元素
let div = document.createElement('div');
// 2. 將元素的類設(shè)置為 "alert"
div.className = "alert";
// 3. 填充消息內(nèi)容
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
我們已經(jīng)創(chuàng)建了該元素。但到目前為止,它還只是在一個名為 div
的變量中,尚未在頁面中。所以我們無法在頁面上看到它。
為了讓 div
顯示出來,我們需要將其插入到 document
中的某處。例如,通過 document.body
將其插入到 <body>
元素里。
對此有一個特殊的方法 append
:document.body.append(div)
。
這是完整代碼:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
document.body.append(div);
</script>
在這個例子中,我們對 document.body
調(diào)用了 append
方法。不過我們可以在其他任何元素上調(diào)用 append
方法,以將另外一個元素放入到里面。例如,通過調(diào)用 div.append(anotherElement)
,我們便可以在 <div>
末尾添加一些內(nèi)容。
這里是更多的元素插入方法,指明了不同的插入位置:
node.append(...nodes or strings)
? —— 在 ?node
? 末尾 插入節(jié)點或字符串,node.prepend(...nodes or strings)
? —— 在 ?node
? 開頭 插入節(jié)點或字符串,node.before(...nodes or strings)
? —— 在 ?node
? 前面 插入節(jié)點或字符串,node.after(...nodes or strings)
? —— 在 ?node
? 后面 插入節(jié)點或字符串,node.replaceWith(...nodes or strings)
? —— 將 ?node
? 替換為給定的節(jié)點或字符串。這些方法的參數(shù)可以是一個要插入的任意的 DOM 節(jié)點列表,或者文本字符串(會被自動轉(zhuǎn)換成文本節(jié)點)。
讓我們在實際應(yīng)用中看一看。
下面是使用這些方法將列表項添加到列表中,以及將文本添加到列表前面和后面的示例:
<ol id="ol">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
ol.before('before'); // 將字符串 "before" 插入到 <ol> 前面
ol.after('after'); // 將字符串 "after" 插入到 <ol> 后面
let liFirst = document.createElement('li');
liFirst.innerHTML = 'prepend';
ol.prepend(liFirst); // 將 liFirst 插入到 <ol> 的最開始
let liLast = document.createElement('li');
liLast.innerHTML = 'append';
ol.append(liLast); // 將 liLast 插入到 <ol> 的最末尾
</script>
這張圖片直觀地顯示了這些方法所做的工作:
因此,最終列表將為:
before
<ol id="ol">
<li>prepend</li>
<li>0</li>
<li>1</li>
<li>2</li>
<li>append</li>
</ol>
after
如上所述,這些方法可以在單個調(diào)用中插入多個節(jié)點列表和文本片段。
例如,在這里插入了一個字符串和一個元素:
<div id="div"></div>
<script>
div.before('<p>Hello</p>', document.createElement('hr'));
</script>
請注意:這里的文字都被“作為文本”插入,而不是“作為 HTML 代碼”。因此像 <
、>
這樣的符號都會被作轉(zhuǎn)義處理來保證正確顯示。
所以,最終的 HTML 為:
<p>Hello</p>
<hr>
<div id="div"></div>
換句話說,字符串被以一種安全的方式插入到頁面中,就像 elem.textContent
所做的一樣。
所以,這些方法只能用來插入 DOM 節(jié)點或文本片段。
但如果我們想要將內(nèi)容“作為 HTML 代碼插入”,讓內(nèi)容中的所有標簽和其他東西都像使用 elem.innerHTML
所表現(xiàn)的效果一樣,那應(yīng)該怎么辦呢?
為此,我們可以使用另一個非常通用的方法:elem.insertAdjacentHTML(where, html)
。
該方法的第一個參數(shù)是代碼字(code word),指定相對于 elem
的插入位置。必須為以下之一:
"beforebegin"
? —— 將 ?html
? 插入到 ?elem
? 之前,"afterbegin"
? —— 將 ?html
? 插入到 ?elem
? 開頭,"beforeend"
? —— 將 ?html
? 插入到 ?elem
? 末尾,"afterend"
? —— 將 ?html
? 插入到 ?elem
? 之后。第二個參數(shù)是 HTML 字符串,該字符串會被“作為 HTML” 插入。
例如:
<div id="div"></div>
<script>
div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');
div.insertAdjacentHTML('afterend', '<p>Bye</p>');
</script>
……將導(dǎo)致:
<p>Hello</p>
<div id="div"></div>
<p>Bye</p>
這就是我們可以在頁面上附加任意 HTML 的方式。
這是插入變體的示意圖:
我們很容易就會注意到這張圖片和上一張圖片的相似之處。插入點實際上是相同的,但此方法插入的是 HTML。
這個方法有兩個兄弟:
elem.insertAdjacentText(where, text)
? —— 語法一樣,但是將 ?text
? 字符串“作為文本”插入而不是作為 HTML,elem.insertAdjacentElement(where, elem)
? —— 語法一樣,但是插入的是一個元素。它們的存在主要是為了使語法“統(tǒng)一”。實際上,大多數(shù)時候只使用 insertAdjacentHTML
。因為對于元素和文本,我們有 append/prepend/before/after
方法 —— 它們也可以用于插入節(jié)點/文本片段,但寫起來更短。
所以,下面是顯示一條消息的另一種變體:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
document.body.insertAdjacentHTML("afterbegin", `<div class="alert">
<strong>Hi there!</strong> You've read an important message.
</div>`);
</script>
想要移除一個節(jié)點,可以使用 node.remove()
。
讓我們的消息在一秒后消失:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<script>
let div = document.createElement('div');
div.className = "alert";
div.innerHTML = "<strong>Hi there!</strong> You've read an important message.";
document.body.append(div);
setTimeout(() => div.remove(), 1000);
</script>
請注意:如果我們要將一個元素 移動 到另一個地方,則無需將其從原來的位置中刪除。
所有插入方法都會自動從舊位置刪除該節(jié)點。
例如,讓我們進行元素交換:
<div id="first">First</div>
<div id="second">Second</div>
<script>
// 無需調(diào)用 remove
second.after(first); // 獲取 #second,并在其后面插入 #first
</script>
如何再插入一條類似的消息?
我們可以創(chuàng)建一個函數(shù),并將代碼放在其中。但是另一種方法是 克隆 現(xiàn)有的 div
,并修改其中的文本(如果需要)。
當我們有一個很大的元素時,克隆的方式可能更快更簡單。
調(diào)用 elem.cloneNode(true)
來創(chuàng)建元素的一個“深”克隆 —— 具有所有特性(attribute)和子元素。如果我們調(diào)用 elem.cloneNode(false)
,那克隆就不包括子元素。
一個拷貝消息的示例:
<style>
.alert {
padding: 15px;
border: 1px solid #d6e9c6;
border-radius: 4px;
color: #3c763d;
background-color: #dff0d8;
}
</style>
<div class="alert" id="div">
<strong>Hi there!</strong> You've read an important message.
</div>
<script>
let div2 = div.cloneNode(true); // 克隆消息
div2.querySelector('strong').innerHTML = 'Bye there!'; // 修改克隆
div.after(div2); // 在已有的 div 后顯示克隆
</script>
DocumentFragment
是一個特殊的 DOM 節(jié)點,用作來傳遞節(jié)點列表的包裝器(wrapper)。
我們可以向其附加其他節(jié)點,但是當我們將其插入某個位置時,則會插入其內(nèi)容。
例如,下面這段代碼中的 getListContent
會生成帶有 <li>
列表項的片段,然后將其插入到 <ul>
中:
<ul id="ul"></ul>
<script>
function getListContent() {
let fragment = new DocumentFragment();
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
fragment.append(li);
}
return fragment;
}
ul.append(getListContent()); // (*)
</script>
請注意,在最后一行 (*)
我們附加了 DocumentFragment
,但是它和 ul
“融為一體(blends in)”了,所以最終的文檔結(jié)構(gòu)應(yīng)該是:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
DocumentFragment
很少被顯式使用。如果可以改為返回一個節(jié)點數(shù)組,那為什么還要附加到特殊類型的節(jié)點上呢?重寫示例:
<ul id="ul"></ul>
<script>
function getListContent() {
let result = [];
for(let i=1; i<=3; i++) {
let li = document.createElement('li');
li.append(i);
result.push(li);
}
return result;
}
ul.append(...getListContent()); // append + "..." operator = friends!
</script>
我們之所以提到 DocumentFragment
,主要是因為它上面有一些概念,例如 template 元素,我們將在以后討論。
老式用法
這些內(nèi)容有助于理解舊腳本,但不適合用于新代碼的開發(fā)中。
由于歷史原因,還存在“老式”的 DOM 操作方法。
這些方法來自真正的遠古時代。如今,沒有理由再使用它們了,因為諸如 append
,prepend
,before
,after
,remove
,replaceWith
這些現(xiàn)代方法更加靈活。
我們在這兒列出這些方法的唯一原因是,你可能會在許多腳本中遇到它們。
?parentElem.appendChild(node)
?
將 node
附加為 parentElem
的最后一個子元素。
下面這個示例在 <ol>
的末尾添加了一個新的 <li>
:
<ol id="list">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
let newLi = document.createElement('li');
newLi.innerHTML = 'Hello, world!';
list.appendChild(newLi);
</script>
?parentElem.insertBefore(node, nextSibling)
?
在 parentElem
的 nextSibling
前插入 node
。
下面這段代碼在第二個 <li>
前插入了一個新的列表項:
<ol id="list">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
let newLi = document.createElement('li');
newLi.innerHTML = 'Hello, world!';
list.insertBefore(newLi, list.children[1]);
</script>
如果要將 newLi
插入為第一個元素,我們可以這樣做:
list.insertBefore(newLi, list.firstChild);
?parentElem.replaceChild(node, oldChild)
?
將 parentElem
的后代中的 oldChild
替換為 node
。
?parentElem.removeChild(node)
?
從 parentElem
中刪除 node
(假設(shè) node
為 parentElem
的后代)。
下面這個示例從 <ol>
中刪除了 <li>
:
<ol id="list">
<li>0</li>
<li>1</li>
<li>2</li>
</ol>
<script>
let li = list.firstElementChild;
list.removeChild(li);
</script>
所有這些方法都會返回插入/刪除的節(jié)點。換句話說,parentElem.appendChild(node)
返回 node
。但是通常我們不會使用返回值,我們只是使用對應(yīng)的方法。
還有一個非常古老的向網(wǎng)頁添加內(nèi)容的方法:document.write
。
語法如下:
<p>Somewhere in the page...</p>
<script>
document.write('<b>Hello from JS</b>');
</script>
<p>The end</p>
調(diào)用 document.write(html)
意味著將 html
“就地馬上”寫入頁面。html
字符串可以是動態(tài)生成的,所以它很靈活。我們可以使用 JavaScript 創(chuàng)建一個完整的頁面并對其進行寫入。
這個方法來自于沒有 DOM,沒有標準的上古時期……。但這個方法依被保留了下來,因為還有腳本在使用它。
由于以下重要的限制,在現(xiàn)代腳本中我們很少看到它:
document.write
調(diào)用只在頁面加載時工作。
如果我們稍后調(diào)用它,則現(xiàn)有文檔內(nèi)容將被擦除。
例如:
<p>After one second the contents of this page will be replaced...</p>
<script>
// 1 秒后調(diào)用 document.write
// 這時頁面已經(jīng)加載完成,所以它會擦除現(xiàn)有內(nèi)容
setTimeout(() => document.write('<b>...By this.</b>'), 1000);
</script>
因此,在某種程度上講,它在“加載完成”階段是不可用的,這與我們上面介紹的其他 DOM 方法不同。
這是它的缺陷。
還有一個好處。從技術(shù)上講,當在瀏覽器正在讀?。ā敖馕觥保﹤魅氲?HTML 時調(diào)用 document.write
方法來寫入一些東西,瀏覽器會像它本來就在 HTML 文本中那樣使用它。
所以它運行起來出奇的快,因為它 不涉及 DOM 修改。它直接寫入到頁面文本中,而此時 DOM 尚未構(gòu)建。
因此,如果我們需要向 HTML 動態(tài)地添加大量文本,并且我們正處于頁面加載階段,并且速度很重要,那么它可能會有幫助。但實際上,這些要求很少同時出現(xiàn)。我們可以在腳本中看到此方法,通常是因為這些腳本很舊。
document.createElement(tag)
? —— 用給定的標簽創(chuàng)建一個元素節(jié)點,document.createTextNode(value)
? —— 創(chuàng)建一個文本節(jié)點(很少使用),elem.cloneNode(deep)
? —— 克隆元素,如果 ?deep==true
? 則與其后代一起克隆。node.append(...nodes or strings)
? —— 在 ?node
? 末尾插入,node.prepend(...nodes or strings)
? —— 在 ?node
? 開頭插入,node.before(...nodes or strings)
? —— 在 ?node
? 之前插入,node.after(...nodes or strings)
? —— 在 ?node
? 之后插入,node.replaceWith(...nodes or strings)
? —— 替換 ?node
?。node.remove()
? —— 移除 ?node
?。文本字符串被“作為文本”插入。
parent.appendChild(node)
?parent.insertBefore(node, nextSibling)
?parent.removeChild(node)
?parent.replaceChild(newElem, node)
?這些方法都返回 ?node
?。
html
? 中給定一些 HTML,?elem.insertAdjacentHTML(where, html)
? 會根據(jù) ?where
? 的值來插入它:"beforebegin"
? —— 將 ?html
? 插入到 ?elem
? 前面,"afterbegin"
? —— 將 ?html
? 插入到 ?elem
? 的開頭,"beforeend"
? —— 將 ?html
? 插入到 ?elem
? 的末尾,"afterend"
? —— 將 ?html
? 插入到 ?elem
? 后面。另外,還有類似的方法,elem.insertAdjacentText
和 elem.insertAdjacentElement
,它們會插入文本字符串和元素,但很少使用。
document.write(html)
?頁面加載完成后,這樣的調(diào)用將會擦除文檔。多見于舊腳本。
我們有一個空的 DOM 元素 elem
和一個字符串 text
。
下面這 3 個命令中的哪些命令會執(zhí)行完全相同的操作?
elem.append(document.createTextNode(text))
?elem.innerHTML = text
?elem.textContent = text
?回答:1 和 3。
這兩個命令都會將 text
“作為文本”添加到 elem
中。
這是一個例子:
<div id="elem1"></div>
<div id="elem2"></div>
<div id="elem3"></div>
<script>
let text = '<b>text</b>';
elem1.append(document.createTextNode(text));
elem2.innerHTML = text;
elem3.textContent = text;
</script>
創(chuàng)建一個函數(shù) ?clear(elem)
? 用來移除元素里的內(nèi)容。
<ol id="elem">
<li>Hello</li>
<li>World</li>
</ol>
<script>
function clear(elem) { /* 你的代碼 */ }
clear(elem); // 清除列表
</script>
首先,讓我們看看 錯誤 的做法:
function clear(elem) {
for (let i=0; i < elem.childNodes.length; i++) {
elem.childNodes[i].remove();
}
}
這是行不通的,因為調(diào)用 remove()
會從首端開始移除 elem.childNodes
集合中的元素,因此,元素每次都從索引 0
開始。但是 i
在增加,所以元素就被跳過了。
用 for..of
循環(huán)的結(jié)果也跟上面一樣。
正確的做法是:
function clear(elem) {
while (elem.firstChild) {
elem.firstChild.remove();
}
}
還有一種更簡單的方法,也可以達到我們所要的效果:
function clear(elem) {
elem.innerHTML = '';
}
在下面這個示例中,我們調(diào)用 table.remove()
從文檔中刪除表格。
但如果運行它,你就會看到文本 "aaa"
并沒有被刪除。
這是為什么?
<table id="table">
aaa
<tr>
<td>Test</td>
</tr>
</table>
<script>
alert(table); // 表格,就是它應(yīng)有的樣子
table.remove();
// 為什么 "aaa" 還存在于文檔中?
</script>
這個題目中的 HTML 是錯的。這就是造成怪異現(xiàn)象的原因。
瀏覽器必須自動修復(fù)它。但 <table>
內(nèi)可能會沒有文本:根據(jù)規(guī)范,只允許特定于表格的標簽。所以瀏覽器將 "aaa"
展示在了 <table>
前面。
當我們刪除表格后,文本 "aaa"
仍然存在的原因就很明顯了吧。
通過使用瀏覽器開發(fā)者工具查看 DOM,就可以輕松地回答這個問題。從瀏覽器開發(fā)者工具中我們可以看到,"aaa"
在 <table>
前面。
HTML 標準規(guī)范詳細描述了如何處理錯誤的 HTML,并且瀏覽器的這種行為是正確的。
編寫一個接口,根據(jù)用戶輸入創(chuàng)建一個列表(list)。
對于每個列表項:
prompt
? 向用戶詢問列表項的內(nèi)容。<li>
?,并添加到 ?<ul>
?。Esc
? 鍵,或輸入一個空內(nèi)容)。所有元素應(yīng)該都是動態(tài)創(chuàng)建的。
如果用戶輸入了 HTML 標簽,那么這些內(nèi)容應(yīng)該被視為文本進行后續(xù)處理。
請注意使用 textContent
對 <li>
的內(nèi)容進行賦值的用法。
編寫一個函數(shù) createTree
,從嵌套對象創(chuàng)建一個嵌套的 ul/li
列表(list)。
例如:
let data = {
"Fish": {
"trout": {},
"salmon": {}
},
"Tree": {
"Huge": {
"sequoia": {},
"oak": {}
},
"Flowering": {
"apple tree": {},
"magnolia": {}
}
}
};
語法:
let container = document.getElementById('container');
createTree(container, data); // 將樹創(chuàng)建在 container 中
結(jié)果(樹)看起來像這樣:
選擇下面兩種方式中的一種,來完成這個任務(wù):
container.innerHTML
?。如果這兩種方式你都做,那就太好了。
P.S. 樹上不應(yīng)該有“多余”的元素,例如空的 <ul></ul>
葉子節(jié)點。
遍歷對象的最簡單的方法是使用遞歸。
這里有一棵由嵌套的 ul/li
組成的樹。
編寫代碼,為每個 <li>
添加其后代數(shù)量。跳過葉子節(jié)點(沒有子代的節(jié)點)。
結(jié)果:
為了將文本附加到每個 <li>
中,我們可以改變文本節(jié)點的 data
。
編寫一個函數(shù) createCalendar(elem, year, month)
。
對該函數(shù)的調(diào)用,應(yīng)該使用給定的 year/month 創(chuàng)建一個日歷,并將創(chuàng)建的日歷放入 elem
中。
創(chuàng)建的日歷應(yīng)該是一個表格(table),其中每一周用 <tr>
表示,每一天用 <td>
表示。表格頂部應(yīng)該是帶有星期名的 <th>
:第一天應(yīng)該是 Monday,依此類推,直到 Sunday。
例如,createCalendar(cal, 2012, 9)
應(yīng)該在元素 cal
中生成如下所示的日歷:
P.S. 在這個任務(wù)中,生成一個日歷就可以了,不需要有點擊交互的功能。
我們將表格創(chuàng)建為字符串:"<table>...</table>"
,然后將其賦值給 innerHTML
。
算法如下:
<th>
? 創(chuàng)建帶有星期名的表頭。d = new Date(year, month-1)
?。它是 ?month
? 的第一天(考慮到 JavaScript 中的月份從 ?0
? 開始,而不是從 ?1
? 開始)。d.getDay()
?,前面的幾個單元格是空的。讓我們用 ?<td></td>
? 填充它們。d
?:?d.setDate(d.getDate()+1)
?。如果 ?d.getMonth()
? 還沒到下一個月,那么就將新的單元格 ?<td>
? 添加到日歷中。如果那天是星期日,就添加一個新行 ?“</tr><tr>”
?。<td>
? 補齊。創(chuàng)建一個像這樣的彩色時鐘:
使用 HTML/CSS 進行樣式設(shè)計,JavaScript 僅用來更新元素中的時間。
首先,讓我們編寫 HTML/CSS。
時間的每個組件都有其自己的 <span>
,那將會看起來很棒:
<div id="clock">
<span class="hour">hh</span>:<span class="min">mm</span>:<span class="sec">ss</span>
</div>
另外,我們需要使用 CSS 為它們著色。
函數(shù) update
會刷新時鐘,由 setInterval
每秒調(diào)用一次:
function update() {
let clock = document.getElementById('clock');
let date = new Date(); // (*)
let hours = date.getHours();
if (hours < 10) hours = '0' + hours;
clock.children[0].innerHTML = hours;
let minutes = date.getMinutes();
if (minutes < 10) minutes = '0' + minutes;
clock.children[1].innerHTML = minutes;
let seconds = date.getSeconds();
if (seconds < 10) seconds = '0' + seconds;
clock.children[2].innerHTML = seconds;
}
在 (*)
行中,我們每次都檢查當前時間。setInterval
調(diào)用并不可靠:它們可能會發(fā)生延遲現(xiàn)象。
時鐘管理函數(shù):
let timerId;
function clockStart() { // 運行時鐘
if (!timerId) { // 僅當時鐘停止時 setInterval
timerId = setInterval(update, 1000);
}
update(); // (*)
}
function clockStop() {
clearInterval(timerId);
timerId = null; // (**)
}
請注意,update()
不僅在 clockStart()
中被調(diào)度,而且還立即在 (*)
行運行。否則,訪問者將不得不等到 setInterval
第一次執(zhí)行。在那之前,時鐘將是空的。
此外,在 clockStart()
中僅當時鐘未運行時才進行 setInterval 也是至關(guān)重要的。否則多次點擊 Start 按鈕會設(shè)置多個并發(fā)的間隔。更糟糕的是 —— 我們只會保留最后一個時間間隔的 timerID
,失去對所有其他時間間隔的引用。那我們就再也無法停止時鐘了!請注意,當時鐘停止時,我們需要在 (**)
行這樣清除 timerID
,以便可以通過執(zhí)行 clockStart()
再次啟動時鐘。
編寫代碼,將 <li>2</li><li>3</li>
,插入到兩個 <li>
之間:
<ul id="ul">
<li id="one">1</li>
<li id="two">4</li>
</ul>
當我們需要在某處插入 HTML 時,insertAdjacentHTML
是最適合的方案。
解決方法:
one.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>');
下面是一個表格:
<table>
<thead>
<tr>
<th>Name</th><th>Surname</th><th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>John</td><td>Smith</td><td>10</td>
</tr>
<tr>
<td>Pete</td><td>Brown</td><td>15</td>
</tr>
<tr>
<td>Ann</td><td>Lee</td><td>5</td>
</tr>
<tr>
<td>...</td><td>...</td><td>...</td>
</tr>
</tbody>
</table>
可能會有更多行。
編寫代碼,按 ?"name"
? 列對其進行排序。
這個解決方案雖然很短,但可能看起來有點難理解,因此,在這里我提供了一些擴展性的注釋:
let sortedRows = Array.from(table.tBodies[0].rows) // 1
.sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));
table.tBodies[0].append(...sortedRows); // (3)
對此算法一步一步進行講解:
<tbody>
? 獲取所有 ?<tr>
?。<td>
?(?name
? 字段)中的內(nèi)容進行比較。.append(...sortedRows)
? 按正確的順序插入節(jié)點。我們不必刪除行元素,只需要“重新插入”,它們就會自動離開原來的位置。
P.S. 在我們的例子中,表格中有一個明確的 <tbody>
,但即使 HTML 中的表格沒有 <tbody>
,DOM 結(jié)構(gòu)也總是具有它。
更多建議: