学习前提
理解数组filter、表单事件和安全DOM渲染;先完成课程卡片布局。
本课完成后你能做什么
把关键词和主题条件组合成可预测的筛选,完整处理重置和无匹配结果。
先理解机制
先约定查询规则:关键词在标题中不区分大小写匹配;主题为all时不限制;两个条件同时满足才显示。将规则写清楚能避免界面做出一个选择,数据却按另一种逻辑筛选。示例用少量本地数据,不发送请求。
保留courses原始集合,通过filter派生当前结果。不要删除不匹配项,也不要在每次查询中不断修改原数组。渲染函数读取输入状态,清空旧DOM后按结果创建节点,同时更新条数或空提示。
重置是一项完整状态变化:清除查询、恢复主题、重新渲染,并把焦点返回查询框。role=status让结果数量变化可被辅助技术获知。真实大数据检索应由API分页处理,不把海量内容全部下载到浏览器。
这里结果是文字列表,避免使用没有内容页的假链接。接入实际课程时为每项增加真实url并生成a元素。对URL来源建立允许规则,对标题仍使用textContent,不能直接拼接HTML。
可运行示例
<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Course finder</title>
<style>body{max-width:800px;margin:auto;padding:24px;font:18px/1.6 system-ui}input,select,button{font:inherit;max-width:100%}li{padding:16px;border-bottom:1px solid #ccc}label{display:block}</style>
<main><h1>Course finder</h1><label for="q">Keyword</label><input id="q" type="search"><label for="topic">Topic</label><select id="topic"><option value="all">All</option><option>HTML</option><option>CSS</option><option>JavaScript</option></select><button id="clear">Clear filters</button><p id="status" role="status"></p><ul id="results"></ul></main>
<script>
const courses=[{id:1,title:'Semantic HTML',topic:'HTML'},{id:2,title:'Forms and labels',topic:'HTML'},{id:3,title:'Grid layout',topic:'CSS'},{id:4,title:'Promises',topic:'JavaScript'}];
const query=document.querySelector('#q'),topic=document.querySelector('#topic'),list=document.querySelector('#results'),status=document.querySelector('#status');
function render(){
const q=query.value.trim().toLowerCase();
const items=courses.filter(x=>(topic.value==='all'||x.topic===topic.value)&&x.title.toLowerCase().includes(q));
list.replaceChildren();
for(const item of items){const li=document.createElement('li');li.textContent=`${item.title} — ${item.topic}`;list.append(li);}
status.textContent=items.length?`${items.length} courses`:'No matches. Clear or change your filters.';
}
query.addEventListener('input',render);topic.addEventListener('change',render);
document.querySelector('#clear').addEventListener('click',()=>{query.value='';topic.value='all';render();query.focus();});render();
</script></html>
推演与验证
初始4项;主题HTML得到2项;再输入forms得到1项;输入grid得到0项;清除恢复4项且焦点回到查询框。输入大写PROMISES仍匹配。重复切换条件后原始数据应不变。
常见错误与边界
本地搜索无需强行加入防抖,先测量工作量。若接入网络,不要沿用同步render假设,需要加载、错误、取消与过期响应保护。无结果不是网络错误,两者应不同反馈。
动手练习
增加标题排序开关,排序前复制结果,并保留组合筛选规则。
参考解法与理由
对派生items使用展开复制后localeCompare排序。把排序视作第三个独立状态,再验证清除按钮是恢复默认排序还是保留排序,并在界面保持一致。