input事件比change更适合实时过滤,因其在每次输入、粘贴、删除时立即触发,覆盖所有编辑场景;change需失焦才触发,无法满足即时响应需求。

为什么 input 事件比 change 更适合实时过滤
因为用户每敲一个字都得立刻响应,change 要等失焦才触发,根本没法用。而 input 在输入、粘贴、删除时都会触发,覆盖所有编辑场景。
注意点:input 事件不冒泡,但你绑定在 <input> 上就行,不用考虑委托;另外 iOS Safari 对 input 的触发时机稍有延迟,加个 setTimeout 防抖不是必须,但能避免高频重绘卡顿。
- 别用
keyup:长按重复触发、中文输入法未上屏就匹配,结果错乱 - 防抖推荐用
setTimeout+clearTimeout,不用引入 Lodash - 过滤前先
.trim(),空字符串或纯空格时直接显示全部
如何用 Array.prototype.filter() 做大小写不敏感匹配
原始数据通常是数组对象,比如 [{name: "React"}, {name: "Vue"}],不能直接对整个对象调 includes(),得指定字段。
关键在转换大小写:用 toLowerCase() 统一处理搜索词和目标字段值,比正则 /.../i 更轻量、无兼容风险。
- 别写
item.name.includes(searchText)—— 大小写不匹配就漏掉 - 正确写法:
item.name.toLowerCase().includes(searchText.toLowerCase()) - 如果要支持多字段(如 name 和 desc),用
||连接多个includes判断 - 中文、数字、符号无需额外处理,
toLowerCase()对它们无影响
DOM 更新时为什么不要反复 innerHTML = '' 再拼接
每次赋值 innerHTML 都会触发完整 DOM 解析和重排,列表一多(比如 200+ 条)就明显卡顿。更稳的方式是只操作需要更新的部分。
实操建议:提前把列表项包裹进一个 <div id="list-container">,过滤后只清空它,再用 <code>document.createElement + appendChild 批量插入新节点;或者用 DocumentFragment 缓存所有新节点再一次性挂载。
- 避免在循环里反复读写
innerHTML,哪怕只是追加 - 如果用模板字符串拼 HTML,记得对数据做
textContent级转义,防止 XSS(比如用document.createTextNode(text).textContent替代直接插值) - 显示“无结果”提示,别靠 CSS
hidden控制——直接在 JS 里控制容器的textContent或插入提示<p></p>
移动端点击空白处收起键盘却清空搜索框?这是 blur 误触发
iOS 和部分安卓浏览器在软键盘收起时会触发 blur,如果你监听了 blur 并清空了输入框,用户刚输一半就被清掉了。
真正该清空的时机只有两个:用户主动点“清空”按钮,或按了 ESC 键。其他时候保留输入内容,方便继续编辑或切后台再回来。
- 删掉所有对
blur的清空逻辑 - ESC 清空:监听
keydown,判断e.key === 'Escape' - 清空按钮用
type="button",避免表单默认行为干扰 - 输入框加
autocomplete="off"和autocorrect="off"减少输入法干扰











