前情提要? 上一篇實作方法利用了三個迴圈,取得標題,將標題的資料利用索引(index) 抓取表格的內容,
程式碼如下:
let headData = Array.from(document.querySelectorAll('thead > tr > th')).map(n=>n.innerText)
console.log('headData', headData )
let tableObject = []
for(let trNode of document.querySelectorAll('tbody > tr') ){
let data = {};
Array.from(trNode.querySelectorAll('td')).forEach((n , index)=>{
data[ headData[index] ] = n.innerText
})
tableObject.push(data)
}
console.log( 'tableObject ',tableObject )
其中內容可以看到,第二段的地方,所跑的迴圈是針對 td 來做處理,headData 的部分其實也是針對 tr 下的 欄位去做處理,以前針對 XHTML 有稍微看一下規範,在標準的 HTML 結構下 TABLE 的子節點必須是 caption,colgroup,thead,tbody,tr 幾種節點,tr 這個節點下也只能有 th 跟 td 在這個標準規範下,我們理論上可以信任瀏覽器會解析這些元素來使用
所以現在要做的事情就是 拿出 tr 下的 子節點,在查詢 MDN 文件的時候,可以看到文件底部有一個 Specifications ,引導到 html.spec 網站去,這網站制定了 HTML 的規則,在這邊可以查到標準的文件,雖然很難閱讀…
不過我利用 console.dir( 把 trNode) 給印出來,比照規格書發現了 tr.cells 的屬性,裡面放了允許的子元素 th跟td ,而且也不分標籤內容,因此這邊就可以快速的簡化 tr 下的目標
let headData = Array.from(document.querySelector('thead > tr').cells).map(n=>n.innerText)
這樣標題乍看沒太大改變,但實際上後面會有另外的用途
let tableObject = []
for(let trNode of document.querySelectorAll('tbody > tr') ){
console.dir(trNode)
let tdData = Array.from(trNode.cells).map(n=>n.innerText)
tableObject.push(tdData)
}
第二段把 td的所有資料轉成跟 headData的類型一樣,的陣列數值,這樣兩個方法就做一樣的事情,後面就可以一併簡化
但是 tdData 要如何把陣列加入 KEY 變成物件 push 到 tableObject 呢?google “陣列轉換為物件” 可以找到一個 方法 reduce ,reduce 一開始有點難以理解,這邊推薦一個小工具 https://pythontutor.com ,連進去貼上一個簡單的範例,
let head = ["Company","Contact","Country"];
let data = [
[1,2,3],
[4,5,6]
];
data.forEach( (oneRaw) => {
let s = oneRaw.reduce( (newData,itemData)=>{
console.log( newData,itemData )
return newData
},{})
console.log(s)
})
這段程式碼最大化模擬運作的變數變化數值等等資料,基本上就是 123 本身是一個陣列,對應 td 的資料,456 亦同,123跟456都屬於另一個變數 data 二維陣列中的陣列,所以把他們跑過一次,根據 console.log 的顯示結果 newData 會根據 return 一直傳遞到下一個 newData,itemData,會傳遞他的陣列中的數值,reduce 在傳遞的時候有三個參數值,可以利用這個參數值取得目前 array 的 index 數值,所以要讓他可以動的程式碼如下:

let head = ["Company","Contact","Country"];
let data = [
[1,2,3],
[4,5,6]
];
data.forEach( (oneRaw) => {
let s = oneRaw.reduce( (newData,itemData,index)=>{
newData[ head[index] ] = itemData
return newData
},{})
console.log(s)
})
一樣 給索引值之後利用 head 的 value 當成 key值,最後給予 數值

測試完了以後就可以引用到程式碼中
let tableObject = []
for(let trNode of document.querySelectorAll('tbody > tr') ){
console.dir(trNode)
let tdData = Array.from(trNode.cells).map(n=>n.innerText)
let tdItem = oneRaw.reduce( (newData,itemData,index)=>{
newData[ head[index] ] = itemData
return newData
},{})
tableObject.push(tdItem )
}
剛剛有提到兩個重複的 function 方法我們可以合併一下內容,最後把不需要的變數整理一下
function trNodeToArray( trNode ){
return Array.from(trNode.cells).map(n=>n.innerText)
}
let headData = trNodeToArray( document.querySelector('thead > tr') )
console.log('headData', headData )
let tableObject = []
for(let trNode of document.querySelectorAll('tbody > tr') ){
console.dir(trNode)
let tdData = trNodeToArray(trNode).reduce( (newData,itemData,index)=>{
newData[ headData[index] ] = itemData
return newData
},{})
tableObject.push( tdData )
}
console.log( 'tableObject ',tableObject )
最後雖然把一樣的方法抽離出來了,但是看起來很怪,如何能確定 抽離出來使用這個 trNodeToArray 的物件塞入的參數就是 tr 節點呢?這個時候就需要原型鏈的用法…