10

5分钟给Hugo博客增加搜索功能

 3 years ago
source link: https://ttys3.dev/post/hugo-fast-search/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

相关文件

注意:跟原文章相比,老灯做了一些微调

{{- $.Scratch.Add "index" slice -}}
{{- range .Site.RegularPages -}}
    {{- $.Scratch.Add "index" (dict "title" .Title "tags" .Params.tags "categories" .Params.categories "contents" .Plain "permalink" .Permalink "date" .Date "section" .Section) -}}
{{- end -}}
{{- $.Scratch.Get "index" | jsonify -}}

这里默认取的contents, 如果文章数量特别多,可能会导致生成的索引过大

[outputs]
  home = ["HTML", "RSS", "JSON"]

fuse.min.js 可从 https://github.com/krisk/Fuse/releases 下载。

var fuse; // holds our search engine
var fuseIndex;
var searchVisible = false; 
var firstRun = true; // allow us to delay loading json data unless search activated
var list = document.getElementById('searchResults'); // targets the <ul>
var first = list.firstChild; // first child of search list
var last = list.lastChild; // last child of search list
var maininput = document.getElementById('searchInput'); // input box for search
var resultsAvailable = false; // Did we get any search results?

// ==========================================
// The main keyboard event listener running the show
//
document.addEventListener('keydown', function(event) {

  // CMD-/ to show / hide Search
  if (event.altKey && event.which === 191) {
      // Load json search index if first time invoking search
      // Means we don't load json unless searches are going to happen; keep user payload small unless needed
      doSearch(event)
  }

  // Allow ESC (27) to close search box
  if (event.keyCode == 27) {
    if (searchVisible) {
      document.getElementById("fastSearch").style.visibility = "hidden";
      document.activeElement.blur();
      searchVisible = false;
    }
  }

  // DOWN (40) arrow
  if (event.keyCode == 40) {
    if (searchVisible && resultsAvailable) {
      console.log("down");
      event.preventDefault(); // stop window from scrolling
      if ( document.activeElement == maininput) { first.focus(); } // if the currently focused element is the main input --> focus the first <li>
      else if ( document.activeElement == last ) { last.focus(); } // if we're at the bottom, stay there
      else { document.activeElement.parentElement.nextSibling.firstElementChild.focus(); } // otherwise select the next search result
    }
  }

  // UP (38) arrow
  if (event.keyCode == 38) {
    if (searchVisible && resultsAvailable) {
      event.preventDefault(); // stop window from scrolling
      if ( document.activeElement == maininput) { maininput.focus(); } // If we're in the input box, do nothing
      else if ( document.activeElement == first) { maininput.focus(); } // If we're at the first item, go to input box
      else { document.activeElement.parentElement.previousSibling.firstElementChild.focus(); } // Otherwise, select the search result above the current active one
    }
  }
});


// ==========================================
// execute search as each character is typed
//
document.getElementById("searchInput").onkeyup = function(e) { 
  executeSearch(this.value);
}

document.querySelector("body").onclick = function(e) { 
    if (e.target.tagName === 'BODY' || e.target.tagName === 'DIV') {
        hideSearch()
    }
}

document.querySelector("#search-btn").onclick = function(e) { 
    doSearch(e)
}
  
function doSearch(e) {
    e.stopPropagation();
    if (firstRun) {
        loadSearch() // loads our json data and builds fuse.js search index
        firstRun = false // let's never do this again
    }
    // Toggle visibility of search box
    if (!searchVisible) {
        showSearch() // search visible
    }
    else {
        hideSearch()
    }
}

function hideSearch() {
    document.getElementById("fastSearch").style.visibility = "hidden" // hide search box
    document.activeElement.blur() // remove focus from search box 
    searchVisible = false
}

function showSearch() {
    document.getElementById("fastSearch").style.visibility = "visible" // show search box
    document.getElementById("searchInput").focus() // put focus in input box so you can just start typing
    searchVisible = true
}

// ==========================================
// fetch some json without jquery
//
function fetchJSONFile(path, callback) {
  var httpRequest = new XMLHttpRequest();
  httpRequest.onreadystatechange = function() {
    if (httpRequest.readyState === 4) {
      if (httpRequest.status === 200) {
        var data = JSON.parse(httpRequest.responseText);
          if (callback) callback(data);
      }
    }
  };
  httpRequest.open('GET', path);
  httpRequest.send(); 
}


// ==========================================
// load our search index, only executed once
// on first call of search box (CMD-/)
//
function loadSearch() { 
  console.log('loadSearch()')
  fetchJSONFile('/index.json', function(data){

    var options = { // fuse.js options; check fuse.js website for details
      shouldSort: true,
      location: 0,
      distance: 100,
      threshold: 0.4,
      minMatchCharLength: 2,
      keys: [
        'permalink',
        'title',
        'tags',
        'contents'
        ]
    };
    // Create the Fuse index
    fuseIndex = Fuse.createIndex(options.keys, data)
    fuse = new Fuse(data, options, fuseIndex); // build the index from the json file
  });
}


// ==========================================
// using the index we loaded on CMD-/, run 
// a search query (for "term") every time a letter is typed
// in the search box
//
function executeSearch(term) {
  let results = fuse.search(term); // the actual query being run using fuse.js
  let searchitems = ''; // our results bucket

  if (results.length === 0) { // no results based on what was typed into the input box
    resultsAvailable = false;
    searchitems = '';
  } else { // build our html
    // console.log(results)
    permalinks = [];
    numLimit = 5;
    for (let item in results) { // only show first 5 results
        if (item > numLimit) {
            break;
        }
        if (permalinks.includes(results[item].item.permalink)) {
            continue;
        }
    //   console.log('item: %d, title: %s', item, results[item].item.title)
      searchitems = searchitems + '<li><a href="' + results[item].item.permalink + '" tabindex="0">' + '<span class="title">' + results[item].item.title + '</span></a></li>';
      permalinks.push(results[item].item.permalink);
    }
    resultsAvailable = true;
  }

  document.getElementById("searchResults").innerHTML = searchitems;
  if (results.length > 0) {
    first = list.firstChild.firstElementChild; // first result container — used for checking against keyboard up/down location
    last = list.lastChild.firstElementChild; // last result container — used for checking against keyboard up/down location
  }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK