Angular SPA表格排序无需重插DOM且保留响应性的实现方法
问题描述
有一个Angular SPA类型的Web应用展示了一个大容量表格,但自带功能不支持排序,我打算通过浏览器控制台实现排序功能。目前我已经完成了排序逻辑的编写,但重新插入表格行的操作会导致原有响应性(绑定的事件监听器)丢失,完全失去实用意义。
现有实现代码
JS代码
function sortRows() { const table = document.querySelector('#mytable'); const tbody = table.querySelector('tbody'); const rows = tbody.querySelectorAll('tr'); const rowsSortedBySize = Array.from(rows).sort((a, b) => { return Number(b.children[3].textContent) - Number(a.children[3].textContent); }); // 移除原有行 tbody.innerHTML = ""; // 插入排序后的行 rowsSortedBySize.forEach(row => tbody.appendChild(row)); }
HTML代码
<table id="mytable"> <tbody> <tr> <td><input type="checkbox" name="one"></td> <td>name</td> <td>description</td> <td>1</td> <td>date</td> </tr> <tr> <td><input type="checkbox" name="two"></td> <td>name</td> <td>description</td> <td>2</td> <td>date</td> </tr> </tbody> </table> <button onclick="sortRows()">Sort Rows</button>
解决方案
原有代码的问题出在tbody.innerHTML = ""这一步,该操作会直接销毁tbody下所有DOM节点对应的Angular内部绑定关系,即使后续把持有引用的tr重新插回页面,原有框架绑定的事件、响应式关联也已经被破坏。
以下两种方案都不需要销毁原有DOM节点,可保留所有响应性:
方案1:直接移动DOM节点(无需清空tbody)
DOM原生的appendChild方法本身支持移动已存在的节点:如果传入的节点已经是当前文档的一部分,会先从原位置自动移除,再插入到新位置,完全不需要手动清空tbody,原有绑定不会受影响。
修改后的代码:
function sortRows() { const table = document.querySelector('#mytable'); const tbody = table.querySelector('tbody'); const rows = tbody.querySelectorAll('tr'); const rowsSortedBySize = Array.from(rows).sort((a, b) => { return Number(b.children[3].textContent) - Number(a.children[3].textContent); }); // 直接按排序顺序追加节点,原有节点会自动移动位置 rowsSortedBySize.forEach(row => tbody.appendChild(row)); }
方案2:CSS排序(完全不修改DOM结构,适配性最强)
如果担心操作DOM顺序依然会干扰Angular的变更检测逻辑,可以用Flex布局+order属性实现纯视觉排序,DOM结构完全不变,所有框架绑定的响应性、事件100%保留。
实现代码:
function sortRows() { const table = document.querySelector('#mytable'); const tbody = table.querySelector('tbody'); // 给tbody设置flex布局 tbody.style.display = "flex"; tbody.style.flexDirection = "column"; const rows = Array.from(tbody.querySelectorAll('tr')); // 排序后给每个tr设置order属性即可实现视觉排序 rows.sort((a, b) => { return Number(b.children[3].textContent) - Number(a.children[3].textContent); }).forEach((row, index) => { row.style.order = index; }); }
内容的提问来源于stack exchange,提问作者eozzy
相关产品推荐
相关产品推荐

