Original link: https://yihui.org/en/2023/09/fuse-search/
Normally web searching is performed on the server side. That is, when you send a
query with some keywords, the server will search its database that contains all
data of a website. Searching can also be performed on the client side, ie, in
your web browser via JavaScript. Several JS libraries can do this job, such as
lunr.js and fuse.js . They are not
designed specifically for site searching but are more general-purpose—web
searching is just one possible application.
In this post, I will show three quick steps to support searching on a static
website. I’m using Hugo as the example, but the same code can be applied to any
static site generator as long as it can generate the JSON database described in
the first section below.
The JSON database
A static site does not have a database backend. All pages are plain HTML files.
We need to write the content of these files to a JSON file that can be fed into
the JS searching library later. For Hugo sites, this can be easily achieved by a
template layouts/_default/index.json.json
: 1
[ , ]
With the range
loop, we can write the URL, title, and plain content of all
regular pages to a JSON file that looks like this:
[ {"uri": "/foo/", "title": "Hi foo", "content": "This is foo."}, {"uri": "/bar/", "title": "Hi bar", "content": "This is bar."} ]
The output config
To tell Hugo to actually generate the aforementioned index.json
file, you need
to specify the outputs
field in your config file ( config.yaml
or
hugo.yaml
): 2
outputs: home: ["html", "rss", "json"]
You can see this Git
commit for an example.
The search page
Next you add a search page to your site, eg, search.md
under the content/
directory (or any subdirectory—the location does not matter). In the body of
this .md
file, you include the following three pieces of code. 3
The HTML (UI)
First, we provide a search input with an ID search-input
, and a container with
a class search-results
to show the search results:
<input type="search" id="search-input"> <div class="search-results"> <section> <h2><a target="_blank"></a></h2> <div class="search-preview"></div> </section> </div>
Inside the container, we provide a template to display each result. This
template should be a single HTML element (eg, a <section>
above) that
contains at least two elements: <a>
to provide links to result pages, and an
element with a class search-preview
to display a short preview of the page
content. You can add other elements to the template freely if you want.
The JavaScript
Next, we load the fuse.js library, and a JS script that I wrote to implement
site search: 4
<script src="https://cdn.jsdelivr.net/npm/[email protected]" defer></script> <script src="https://cdn.jsdelivr.net/npm/@xiee/utils/js/fuse-search.min.js" defer></script>
Styling (optional)
Last, you may want to style your search input and results with CSS, although
this is optional. Below I’m making the input full-width, increasing its font
size, highlighting keywords in results (wrapped in <b></b>
), and indenting the
preview text:
<style type="text/css"> #search-input { width: 100%; font-size: 1.2em; padding: .5em; } .search-results b { background-color: yellow; } .search-preview { margin-left: 2em; } </style>
You may see this Git
commit for an example that
includes the HTML, JS, and CSS.
Now you can visit this search page on your website and start searching. You can
include the link to this page in your site
menu to make it easier to
discover. I added the link to the bottom menu of my site.
Customization
Besides styling the HTML UI via CSS, there are a few more options for you to
customize the search behavior, which can be written as data-
attributes in the
<input>
element (all of them have default values):
-
data-info-init
: the placeholder text of the input when the search index is
being initialized, which happens as soon as the cursor is moved into the
input; -
data-info-ok
: the placeholder text after the search index is successfully
initialized; -
data-info-fail
: the placeholder text when the initialization fails; -
data-index-url
: the URL toindex.json
; -
data-text-length
: the length of the preview text; -
data-limit
: the maximum number of results to return; -
data-delay
: the delay (in milliseconds) after the last key is lifted
before searching (this is to avoid searching too frequently when typing). On
mobile devices, the searching is performed only after hittingEnter
; on
Other devices, searching is done after a short delay while typing.
Below is an example:
<input type="search" id="search-input" data-info-init="Initializing... Please hold on." data-text-length=500 data-limit=100 data-delay=300>
You can also write more information on your search page, such as how to use the
search box (like what I
did ).
Please feel free to fiddle with any of the code above if the existing options
for customization do not meet your need. Happy searching (your own site or
mine )!
-
This
layouts
folder can be under either the root directory of your Hugo
project, or your theme directory. I recommend the former. If your theme has
already provided this JSON template, you do not need to provide another
copy. ︎ -
Similarly, if you use the TOML format instead of YAML, specify this in
config.toml
orhugo.toml
:[outputs] home = ["html", "rss", "json"]
-
Please note that you must turn on the
unsafe
option for the Markdown
renderer in your site config file, eg,markup: goldmark: renderer: unsafe: true
This is because we are writing raw HTML code in the Markdown file, and Hugo
disallows HTML code by default. ︎ -
If you want searching to work offline, you can download these scripts to
thestatic/
folder of your Hugo site project, and use relative URLs to
include them, eg,<script src="/js/fuse.js" defer></script> <script src="/js/fuse-search.min.js" defer></script>
This article is reproduced from: https://yihui.org/en/2023/09/fuse-search/
This site is only for collection, and the copyright belongs to the original author.