跳至內容
Astera Technology

DocumentFragment 加入頁面後,自己會變空

DocumentFragment 插入後,追蹤子節點搬到哪裏。

下載完整個案資料及參考答案 ↓

先看這個固定例子

先在 fragment 建立兩個清單項目,記住第一個節點參照,再加入 notes。

完整頁面內容與固定程式
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<ul id="notes"></ul><output id="result"></output>
<script>const fragment=document.createDocumentFragment();for(const text of ['原始檔','預覽']){const item=document.createElement('li');item.textContent=text;fragment.append(item);}const first=fragment.firstChild;const notes=document.querySelector('#notes');notes.append(fragment);
const result={fragmentChildren:fragment.childNodes.length,listChildren:notes.children.length,sameNode:first===notes.firstChild};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
確實的 console 輸出
{"fragmentChildren":0,"listChildren":2,"sameNode":true}
輸入與原生結果
核對項目所給輸入或動作結果
加入後 fragmentchildNodes.length0
目標清單children.length2
第一項參照first===notes.firstChildtrue

原生機制怎樣處理

插入 fragment,搬過去的是子節點;fragment 不會變成外層容器,完成後自己已空。第一項仍是同一節點,只改以 notes 為父層。本例不推論效能或顯示速度。

比較另一寫法

把已空的同一 fragment 再放到另一清單,不會複製第一份清單,第二份仍是零項。

另開的反例原始碼
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<ul id="first"></ul><ul id="second"></ul><output id="result"></output>
<script>const fragment=document.createDocumentFragment();for(const text of ['原始檔','預覽']){const item=document.createElement('li');item.textContent=text;fragment.append(item);}document.querySelector('#first').append(fragment);document.querySelector('#second').append(fragment);
const result={first:document.querySelector('#first').children.length,second:document.querySelector('#second').children.length,fragment:fragment.childNodes.length};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
反例輸出
{"first":2,"second":0,"fragment":0}

試清邊界

第一清單保留原節點,第二清單也要同樣內容,應在何時複製?

查看參考答案

在 fragment 尚有子節點時先複製,再分別加入兩份 fragment。每份清單都有兩項,第一節點卻是不同物件。

另開的邊界原始碼
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<ul id="first"></ul><ul id="second"></ul><output id="result"></output>
<script>const fragment=document.createDocumentFragment();for(const text of ['原始檔','預覽']){const item=document.createElement('li');item.textContent=text;fragment.append(item);}const copy=fragment.cloneNode(true);const first=document.querySelector('#first'),second=document.querySelector('#second');first.append(fragment);second.append(copy);
const result={first:first.children.length,second:second.children.length,sameNode:first.firstChild===second.firstChild,originalFragment:fragment.childNodes.length,copiedFragment:copy.childNodes.length};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
邊界輸出
{"first":2,"second":2,"sameNode":false,"originalFragment":0,"copiedFragment":0}

原始例子檔案