MDN 的搜索结果自动补全是怎么做的?(二)

359 阅读2分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

本文翻译自:hacks.mozilla.org/2021/08/mdn… ,作者:Peter Bengtsson

MDN Web Docs 中新添加了一个搜索结果自动补全的功能,这使你可以通过键入文档标题的一部分来快速直接跳转到您要查找的文档。这是关于这个功能如何做的的文章。如果你坚持到底,我将分享一个“复活节彩蛋”功能。

image.png

系列文章

MDN 的搜索结果自动补全是怎么做的?(一)

实现细节

默认情况下,唯一加载的 JavaScript 代码是一个小片段,用于监视搜索 <input> 字段的 onmouseoveronfocusdocument 上还有一个事件侦听器,用于查找确定的击键。在任何时候按下 / ,就像您使用鼠标光标将焦点放在 <input> 中效果一样。

一旦焦点被触发,它做的第一件事就是下载两个 JavaScript 包,将 <input> 字段变成更高级的东西。在最简单的(伪)形式中,它是如何工作的:

<input 
    type="search" 
    name="q" 
    onfocus="startAutocomplete()" 
    onmouseover="startAutocomplete()" 
    placeholder="Site search..." 
    value="q"
>
let started = false; 
function startAutocomplete() { 
    if (started) { 
        return false; 
    } 
    const script = document.createElement("script"); 
    script.src = "https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/static/js/autocomplete.js"; 
    document.head.appendChild(script); 
}

然后它加载了 /static/js/autocomplete.js 这就是真正的魔法发生的地方。让我们深入挖掘伪代码

(async function() { 
    const response = await fetch('/en-US/search-index.json'); 
    const documents = await response.json(); 
    const inputValue = document.querySelector( 'input[type="search"]' ).value; 
    const flex = FlexSearch.create(); 
    documents.forEach(({ title }, i) => { 
        flex.add(i, title); 
    }); 
    const indexResults = flex.search(inputValue); 
    const foundDocuments = indexResults.map((index) => documents[index]); 
    displayFoundDocuments(foundDocuments.slice(0, 10)); 
})();

正如你看到的,这是对其实际工作方式的过度简化,但现在还不是深入研究细节的时候,下一篇文章我们会详细分析这段代码。下一步是显示匹配。我们使用(TypeScript) React来做这件事,但下面的伪代码更容易理解:

function displayFoundResults(documents) { 
    const container = document.createElement("ul"); 
    documents.forEach(({url, title}) => { 
        const row = document.createElement("li"); 
        const link = document.createElement("a"); 
        link.href = url; 
        link.textContent = title; 
        row.appendChild(link); 
        container.appendChild(row); 
    }); 
    document.querySelector('#search').appendChild(container); 
}

然后使用一些CSS,将其显示为 <input> 字段下面的一个覆盖层。例如,我们根据inputValue 高亮显示每个标题,当您上下导航时,各种击键事件处理程序负责高亮显示相关行。