如果您的页面包含大量信息,最好允许用户搜索他们可能需要的内容。我这里说的不是搜索数据库,甚至不是搜索 JSON 数据——我指的是在单个渲染的网页上搜索文本。用户已经可以使用内置的浏览器搜索功能来实现这一点,但我们可以通过提供自己的搜索功能来增强此功能,该功能可以过滤页面,使匹配的结果更容易查找和阅读。
这是一个我们将要构建的内容的实时演示
我在我的实际项目中使用了相同的技术:https://freestuff.dev/。
认识 JavaScript!
好吧,您可能已经了解 JavaScript。JavaScript 将处理此过程中的所有交互性。它将……
- 查找我们要搜索的所有内容,
- 监视用户在搜索输入框中输入的内容,
- 过滤可搜索元素的
innerText
, - 测试文本是否包含搜索词(
.includes()
是这里的主要工作者!),以及 - 根据元素是否包含搜索词来切换(父)元素的可见性。
好的,我们有了需求!让我们开始工作吧。
基本标记
让我们假设我们有一个常见问题页面。每个问题都是一个“卡片”,包含标题和内容
<h1>FAQ Section</h1>
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>What we do</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized </p>
</div>
<div class="cards">
<h3>Why work here</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
</div>
<div class="cards">
<h3>Learn more</h3>
<p>Want to learn more about us?</p>
</div>
想象一下,此页面上有许多问题。
为了准备好交互性,我们将使用这一行 CSS。这为我们提供了一个类,我们可以根据搜索情况在到达 JavaScript 时添加/删除它
.is-hidden { display: none; }
让我们添加一个搜索输入框,并在与之交互时触发事件
<label for="searchbox">Search</label>
<input
type="search"
oninput="liveSearch()"
id="searchbox"
>
JavaScript 基线
这是执行所有其他操作的 JavaScript 代码!
function liveSearch() {
// Locate the card elements
let cards = document.querySelectorAll('.cards')
// Locate the search input
let search_query = document.getElementById("searchbox").value;
// Loop through the cards
for (var i = 0; i < cards.length; i++) {
// If the text is within the card...
if(cards[i].innerText.toLowerCase()
// ...and the text matches the search query...
.includes(search_query.toLowerCase())) {
// ...remove the `.is-hidden` class.
cards[i].classList.remove("is-hidden");
} else {
// Otherwise, add the class.
cards[i].classList.add("is-hidden");
}
}
}
您可能可以逐行查看并推断出它的作用。它找到所有卡片和输入框,并保存对它们的引用。当搜索事件触发时,它会遍历所有卡片,确定文本是否在卡片内。如果卡片中的文本与搜索查询匹配,则会移除 .is-hidden
类以显示卡片;否则,该类将存在,卡片将保持隐藏状态。
这是 演示链接。
添加延迟
为了确保我们的 JavaScript 不会运行太多(这意味着它会降低页面速度),我们将在等待“X”秒后才运行 liveSearch
函数。
<!-- Delete on Input event on this input -->
<label for="searchbox">Search</label>
<input type="search" id="searchbox">
// A little delay
let typingTimer;
let typeInterval = 500; // Half a second
let searchInput = document.getElementById('searchbox');
searchInput.addEventListener('keyup', () => {
clearTimeout(typingTimer);
typingTimer = setTimeout(liveSearch, typeInterval);
});
模糊搜索呢?
假设您想通过用户不可见的文本进行搜索。这个想法有点像模糊搜索,其中相关的关键字会返回与完全匹配相同的搜索结果。这有助于扩展可能“匹配”搜索查询的卡片数量。
有两种方法可以做到这一点。第一种是使用隐藏元素(例如 span),其中包含关键字
<div class="cards">
<h3>Who are we</h3>
<p>It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularized</p>
<!-- Put any keywords here -->
<span class="is-hidden">secret</span>
</div>
然后我们需要更新 liveSearch
函数。我们不会使用 .inner
Text,而是使用 .textContent
来包含隐藏元素。有关 innerText 和 textContent 之间的区别的更多详细信息,请点击此处
for (var i = 0; i < cards.length; i++) {
if(cards[i].textContent.toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
尝试在搜索框中输入“secret”,它应该显示此卡片,即使“secret”没有显示在页面上。
第二种方法是通过属性进行搜索。假设我们有一个图像库。我们可以将关键字直接放在图像的 alt
属性中。尝试在下一个演示中输入“kitten”或“human”。这些查询与图像 alt
文本中包含的内容相匹配。
为了使此功能正常工作,我们需要将 innerText
更改为 getAttribute('alt')
,因为我们希望除了页面上实际显示的内容之外,还要浏览 alt
属性。
for (var i = 0; i < cards.length; i++) {
if(cards[i].getAttribute('alt').toLowerCase()
.includes(search_query.toLowerCase())) {
cards[i].classList.remove("is-hidden");
} else {
cards[i].classList.add("is-hidden");
}
}
根据您的需要,您可以将关键字放在另一个属性中,或者可能是自定义属性中。
注意事项
同样,这并不是一种通过查询数据库或其他数据源来工作的搜索技术。只有当您在该页面上已经渲染的 DOM 中拥有所有可搜索内容时,它才能正常工作。
所以,是的,就是这样。只是一些需要注意的事项。
总结
显然,我非常喜欢这种技术,甚至在生产环境中使用它。但您还可以如何使用类似的技术?正如我们所看到的,常见问题页面是一个明显的候选对象,但任何需要过滤任何类型内容的情况都适合这种方法。甚至图像库也可以使用,使用隐藏输入技巧通过图像的 alt
标签内容进行搜索。
无论如何,我希望您觉得这篇文章有帮助。令我惊讶的是,我们可以用几行原生 JavaScript 代码获得一个相当健壮的搜索解决方案。
您之前是否使用过这种技术或类似的技术?您的用例是什么?
我经常使用表格过滤器和列表过滤器,当获取所有结果比分页更快时,因为过滤比搜索更快。您使用的那个不适用于我的情况,因为 IE 在我的世界中仍然存在。
https://gist.github.com/miwebguy/417160798c8c1b275a0d15d04bf01954
感谢 Bill 分享
感谢您提供这个很棒的教程!我自己也经常使用类似的技术,但它们可能无法完全访问,或者至少不完全符合可访问性标准。
没有提交按钮的表单(例如过滤器表单,在您键入或使用选择元素时立即进行过滤)存在问题,我建议将此链接作为进一步研究的起点:https://stackoverflow.com/questions/42811178/is-it-possible-to-make-a-accessible-form-without-a-submit-button
我自己仍然构建没有提交按钮的过滤器,尤其是在数据已加载的情况下,提交按钮只会通过不必要的额外点击来减慢速度。一些用户可能会觉得这很烦人或难以使用,但这不应该使过滤器无法访问。为了使事情更易于访问,我使用 role=”status” 包含状态消息
您可以在此处阅读有关该属性的信息:https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA22
如果有人更了解如何(或是否)可以使没有提交按钮的过滤器可访问,我们将不胜感激!
感谢 Jonathan 提供的参考
Jonathan 的评论需要加星标。非常有帮助。
可能听起来很俗套,但如果人们期望点击某些东西,那么包含一个搜索按钮会减少混淆。因此,即使搜索是“实时的”,包含一个搜索按钮也可能是一件好事。
当然,还有很多优化空间:例如,保留元素列表而不是每次都重新查询它们。我本来还想说“当输入值不再长时,只过滤可见的元素”,但这在复制/粘贴时不起作用。
很好!如果使用 JSON 进行搜索和内容生成,效果会更好。这样就不需要在 HTML 中隐藏关键词了。
你可以使用 fuse 库,这是一个很棒的模糊搜索库。
我偶然发布了一个 类似但更小(825b)的 npm 包,用于搜索和过滤 JSON 数组。
你可以使用更好的语义 HTML,例如定义列表(DL DT 和 DD 元素)。
此外,使用数据属性 (data-*) 可以更方便地向 HTML 元素添加数据,这些数据旨在通过 JavaScript 进行操作。
如此棒的教程.. mas hilman 厉害!!!
一切正常。但是…
在波兰语中尝试使用“Śreniawa”进行搜索,但无法正常工作… :(
点击“x”移除搜索时,它不起作用。
而且效率低下,你为每个可过滤项都将搜索转换为小写。
当列表变长时,这种方法会对性能产生影响。
我见过一个很棒的解决方案是 Jets.js 所做的,他们操作单个 CSS 规则以隐藏或显示匹配的搜索规则。
因此,如果你搜索“bob”,引擎会动态地写出类似以下内容:
并让 CSS 引擎处理视觉过滤。
适用于较长列表的原生 JS 解决方案。我的列表有 1700 个项目。
let timer;
const input = document.querySelector("#searchbox");
input.addEventListener("keyup", function (liveSearch) {
clearTimeout(timer);
timer = setTimeout(() => {
const items = document.querySelectorAll(".link_button");
for (let item of items) {
item.style.display = item.textContent.includes(liveSearch.target.value)
? "inline-block"
: "none";
}
}, 1000);
});
有没有办法实现类似的功能,但函数是在多个页面上进行搜索。只使用 JavaScript?
嗨!我怎样才能添加一个按钮来显示结果?谢谢
很棒的教程,我用它来在拥有数千行的表格上提供搜索功能!数据是静态的,所以没问题!
在使用 React 时遇到一个问题…
元素在添加事件监听器后才渲染,所以我添加了一个 try/catch 块来解决这个问题。
再次感谢!
我不明白为什么 Bulma 是必需的。如果我删除 Bulma 是必需的吗?那是一个 CSS 库,当我删除它时,JS 不再工作了,但你在文章中没有提到这一点。
有没有办法为它添加动画,使其在过渡方面看起来更漂亮一些?
我怎样才能添加类别进行筛选? :(
我想这是一个基本问题,但我刚开始学习 JS。我一直使用 livesearch,并且有 212 张卡片组成一个会员目录,页面框架是 BS 4.6 和 PHP,当我搜索过滤器时,它是即时的,但我希望显示过滤后的卡片数量,因此获取 JS 总数并传递给 PHP 变量。你能否提供一些关于如何实现此目标的提示?不过脚本很棒。
这对我来说非常有效。谢谢
可以修改它以允许正则表达式搜索吗?