HTML模板本身不执行过滤或排序,所有逻辑必须由JavaScript驱动;需用template.content.cloneNode(true)手动克隆填充,filter/sort始终基于originalData,DOM更新只替换tbody,排序需处理空值、类型和locale,搜索须防抖,优先级逻辑须显式定义。

HTML 模板本身不执行过滤或排序——它只是静态结构,所有逻辑必须由 JavaScript 驱动。真正起作用的是你如何把数据、模板和 DOM 更新三者串起来。
template 标签不是魔法,得配合 JS 才能动态渲染
<template> 用来声明可复用的 HTML 片段,但它不会自动渲染,也不会响应数据变化。你得手动克隆、填充、插入。
- 常见错误:直接在
<template>里写{{name}}或v-for,指望它像 Vue 模板一样工作——不行,原生 template 不解析表达式 - 正确做法:用
template.content.cloneNode(true)获取片段,再用textContent或innerHTML填充字段(注意 XSS,优先用 textContent) - 如果数据含 HTML(如富文本摘要),才用
innerHTML,但必须先做转义:el.innerHTML = DOMPurify.sanitize(item.desc)或手写简单转义函数 - 不要每次循环都调用
document.createElement+appendChild,批量操作用DocumentFragment或一次性tbody.innerHTML = rowsHTML
filter 和 sort 必须基于原始数据,而非当前 DOM
用户点击筛选或排序时,不能只处理当前显示的 <tr> 元素——那些可能已被上次筛选隐藏,或来自分页后的子集。
- 始终维护一份
originalData数组,所有 filter/sort 都从它出发生成新数组 - 避免链式
data.filter(...).filter(...).sort(...):每调用一次filter()就创建新数组,内存和性能双浪费 - 合并条件进单次
filter():比如文本搜索 + 状态多选 + 日期范围,全写在一个回调里用&&连接 - 数值范围筛选前务必校验:
const min = Number(rangeMin.value); if (isNaN(min)) return false;
表头点击排序时,别忘了空值和类型陷阱
字符串默认按 Unicode 排,“10” 会排在 “2” 前面;null 或 undefined 直接参与减法会得 NaN,整个 sort 就乱了。
立即学习“前端免费学习笔记(深入)”;
- 数字字段:用
a.price - b.price,但先确保是数字:Number(a.price) || 0 - 日期字段:统一转时间戳比较:
new Date(a.date).getTime() - new Date(b.date).getTime() - 中文字符串:用
a.name.localeCompare(b.name, 'zh-CN'),比a.name > b.name可靠得多 - 空值处理:约定全部排在最后:
if (a.val == null) return 1; if (b.val == null) return -1; - 升/降序切换靠两个变量:
sortBy = 'name'和isAsc = true,别用字符串'asc'/'desc'增加分支
DOM 更新只动 <tbody>,别碰整张表
每次 filter 或 sort 后,如果执行 table.innerHTML = newHTML,所有 <tr> 会被销毁——正在编辑的 input 失焦、事件监听器丢失、滚动位置重置。
- 只替换
tbody.innerHTML,保留<thead>和<tfoot>不变 - 如果表格支持行内编辑,筛选后被隐藏的行,其
input仍可能处于focus状态,需手动document.activeElement?.blur() - 视觉反馈不能少:排序时给当前
<th>加 class(如sorted-asc),并更新箭头图标;筛选无结果时显示 “暂无匹配数据” 提示 - 防抖不是可选项:文本搜索框必须加
setTimeout或requestIdleCallback,否则连打 5 个字就触发 5 次全量遍历
最常被忽略的一点:排序和筛选的优先级。用户先筛出 20 条,再点姓名排序——该对这 20 条排,还是回原始数据重筛再排?这个逻辑必须在代码里显式定义清楚,不能靠“用户应该懂”。



















