纯前端搜索可行,但仅限小数据(≤1000行)且无权限、分页、关联查询等后端需求;HTML+CSS无法实现动态筛选,因HTML无逻辑能力,CSS无判断功能,实时过滤必须依赖JavaScript读取输入、遍历DOM、执行比对并控制显隐。

前端直接用 JavaScript 驱动,后端必须参与——数据量稍大或涉及权限、分页、关联查询时,纯前端筛就是自欺欺人。
为什么不能只靠 HTML + CSS 完成筛选
HTML 是标记语言,没有判断、循环、状态记忆能力。所谓“点击品牌=Apple 就过滤”,背后全是 JS 在读取 input 值、遍历 tr 或 .product 元素、执行字符串比对或对象属性判断。不写 JS,form 提交也只是跳转或刷新,无法实时显隐内容。
常见错误现象:
- 页面加了多个
input却没绑定事件监听,点筛选按钮毫无反应 - 用
class="filter-brand"模拟筛选状态,结果改个 CSS 类名,整个逻辑就失效 - 把筛选条件拼进 URL 时手动拼字符串,漏掉
encodeURIComponent,中文变乱码
小数据(≤1000 行)怎么用 JS 实时过滤
适用场景:静态表格、商品卡片列表、配置项展示页等所有数据已加载到 DOM 中的情况。核心是用 Array.from() 转换 NodeList,再用 filter() 组合布尔逻辑,避免嵌套 filter().filter() 多次遍历。
立即学习“前端免费学习笔记(深入)”;
关键实操点:
- 用
dataset存结构化字段值(如<div data-brand="apple" data-price="899">),比解析 <code>textContent快且稳定 - 每个筛选控件统一监听
input或change,但必须加防抖(setTimeout+clearTimeout),延迟 300ms,否则移动端打字卡顿 - 空值必须跳过:比如
price_min为空时,不能参与item.price >= minVal判断,否则NaN >= ''返回false - 多选品牌用
<input type="checkbox" name="brand" value="apple">,别用name="brand[]"——FormData不认方括号,后端收不到
示例逻辑片段:
function applyFilter() {
const minVal = +document.getElementById('price-min').value;
const maxVal = +document.getElementById('price-max').value;
const brandSet = new Set(
Array.from(document.querySelectorAll('[name="brand"]:checked'))
.map(cb => cb.value)
);
const products = Array.from(document.querySelectorAll('.product'));
products.forEach(p => {
const price = +p.dataset.price;
const brand = p.dataset.brand;
const matchPrice = (isNaN(minVal) || price >= minVal) &&
(isNaN(maxVal) || price <= maxVal);
const matchBrand = brandSet.size === 0 || brandSet.has(brand);
p.style.display = matchPrice && matchBrand ? 'block' : 'none';
});
}
大数据必须走后端,怎么设计请求和参数
一旦数据来自接口、要分页、含用户权限、或需 JOIN 关联表,前端筛就该立刻让位。此时 HTML 的唯一作用是提供语义化结构,JS 只负责采集和发请求,SQL 构造、校验、分页全由后端完成。
关键设计原则:
- 整个筛选区必须包在
<form id="filter-form"></form>里,不用div—— 否则new FormData(form)无法自动聚合 checkbox 多选、跳过 disabled、忽略空 select -
name属性必须和后端 API 参数名严格一致,比如后端接收?category=phone&in_stock=true,前端就得写<select name="category"></select>和<input type="checkbox" name="in_stock" value="true"> - 价格区间必须拆成两个独立字段:
price_min和price_max,别用单个range或自定义组件再切分 —— 后端没法可靠解析 - 提交前用
form.checkValidity()做基础校验(配合required),比手写正则快且无障碍友好
采集示例:
const form = document.getElementById('filter-form');
form.addEventListener('submit', e => {
e.preventDefault();
const params = Object.fromEntries(new FormData(form).entries());
// { category: "phone", in_stock: "true", price_min: "100" }
fetch('/api/products?' + new URLSearchParams(params))
.then(r => r.json())
.then(data => renderList(data));
});
IndexedDB 场景下怎么处理多条件查
当数据存在本地(如离线 PWA)、又不能全量加载到内存时,IndexedDB 是唯一选择。它不支持 SQL 式 WHERE a AND b AND c,得靠索引 + 游标 + 客户端过滤三者配合。
容易踩的坑:
- 建复合索引时字段顺序不能错:想查
city === "北京" && active === true,索引必须是["city", "active"],反过来就无效 -
IDBKeyRange.only(["北京", true])只能精确匹配,查city === "北京" && age > 25得先用by_city索引缩小范围,再用游标逐条continue()并 JS 判断age - 游标遍历大数据量会阻塞主线程,必须用
requestIdleCallback分批处理,每次最多取 50 条 - 别自己封装游标逻辑,优先用
idb库(npm install idb),它把 Promise、游标、索引操作都封装好了
性能敏感点:如果频繁按不同字段组合查询,预建多个针对性索引(如 by_category_price、by_brand_status)比临时 JS 过滤更稳。



















