使用原生 JavaScript 实现页面内过滤搜索

Avatar of Hilman Ramadhan
Hilman Ramadhan 发表于

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

如果您的页面包含大量信息,最好允许用户搜索他们可能需要的内容。我这里说的不是搜索数据库,甚至不是搜索 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 函数。我们不会使用 .innerText,而是使用 .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 代码获得一个相当健壮的搜索解决方案。

您之前是否使用过这种技术或类似的技术?您的用例是什么?