customelements.define()仅注册元素,过滤逻辑须手动实现;应在connectedcallback中获取shadowroot并监听input事件,用getattribute读取配置,递归提取纯文本避免innertext,支持多列及复杂条件过滤,用hidden替代display优化性能,并更新aria-live。

customElements.define()注册后,过滤逻辑必须手动实现
注册自定义元素只是让浏览器识别新标签,customElements.define()本身不注入任何过滤行为。你不能指望写个 <filter-table></filter-table> 就自动支持关键词搜索或字段联动——所有数据遍历、匹配、DOM 显示/隐藏都得自己写。
常见错误是把过滤逻辑塞进 constructor():此时 shadowRoot 虽已创建,但元素还没挂载到文档,querySelector 找不到输入框,dataset 也读不到初始配置,结果过滤器根本启动不了。
- 在
connectedCallback()中获取this.shadowRoot,再查input和目标表格节点 - 监听
input事件(不是change),避免用户删词时延迟响应 - 用
getAttribute('data-filter-columns')读取多列配置,比dataset.filterColumns更可靠(支持连字符、数字前缀等) - 若需 debounce,直接用
setTimeout+clearTimeout,别依赖第三方库——自定义元素应轻量
多列过滤必须遍历每个 <td>,不能只靠 <code>textContent
真实表格常含按钮、图标、空格、换行符甚至嵌套元素,直接取 td.textContent 容易漏匹配或误匹配。比如 <td>
<span>张三</span><small>(VIP)</small>
</td>,textContent 是 "张三(VIP)",但用户只想搜“张三”。
更稳妥的做法是递归提取纯文本,同时跳过不可见节点(style="display:none" 或 hidden 属性):
function getCellText(node) {
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
return node.textContent;
}
if (node.children.length) {
return Array.from(node.children)
.map(getCellText)
.filter(Boolean)
.join(' ');
}
return '';
}
- 避免用
innerText:它受 CSS 影响,隐藏元素内容可能被忽略,也可能因排版触发重排 - 对搜索词做
.trim().toLowerCase(),再与单元格文本统一转换后比对,减少大小写/空格干扰 - 如果表格有表头(
<th>),默认不参与过滤;如需包含,显式加 <code>data-filter-include-header="true"复杂条件过滤要暴露属性接口,别硬编码规则
当过滤逻辑涉及字段联动(如“状态=待审核时,只显示创建时间 > 7 天的记录”),把规则写死在类里会导致复用困难。正确方式是把条件声明为属性,由外部控制:
- 在
observedAttributes中声明['data-filter-rules'],并在attributeChangedCallback里解析 JSON 字符串 - 允许传入类似
data-filter-rules='[{"field":"status","value":"pending"},{"field":"created_at","op":">","value":"7d"}]' - 用
JSON.parse()前先校验字符串合法性,失败则 fallback 到空数组,避免整个组件崩溃 - 字段路径支持点号(
"user.profile.name"),但必须用getAttribute读原始值,不能依赖dataset(会转驼峰)
注意:这类规则解析应在
connectedCallback和每次属性变更后执行,但不要在每次input事件里重复 parse——缓存解析结果,仅当data-filter-rules变更时更新。过滤性能瓶颈不在 JS,而在 DOM 操作
千行表格实时过滤卡顿,90% 情况不是因为循环慢,而是反复设置
tr.style.display触发重排。尤其当表格在滚动容器内,强制 layout 会明显掉帧。- 用
tr.hidden = true/false替代style.display:更语义化,且部分浏览器优化更好 - 批量操作:收集所有需隐藏/显示的
tr,一次性用DocumentFragment或display: none的父容器包裹再替换 - 超过 500 行时,考虑虚拟滚动:只渲染可视区域行,
scrollTop变化时动态更新tr内容和位置 - 别在过滤函数里调用
getBoundingClientRect()或offsetHeight—— 这些是同步 layout 强制器
最易被忽略的是:过滤后没重置表格的
aria-live区域,屏幕阅读器用户无法感知结果变化。哪怕只加一行this.shadowRoot.querySelector('[aria-live]').textContent = `共找到 ${visibleCount} 条结果`,体验就完全不同。 - 在
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











