2022-12-29 15:28:20 +00:00
// js/fixedsearch.js
2022-12-29 14:55:43 +00:00
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
fixedsearch — Super fast , client side search for Hugo . io with Fusejs . io
based on https : //gist.github.com/cmod/5410eae147e4318164258742dd053993
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
if ( typeof variable !== 'undefined' ) {
console . log ( 'fixedsearch.js already loaded' ) ;
} else {
fixedsearch = function ( ) {
var search _form = document . getElementById ( 'search-form' ) ; // search form
var search _input = document . getElementById ( 'search-input' ) ; // input box for search
var search _submit = document . getElementById ( 'search-submit' ) ; // form submit button
var search _results = document . getElementById ( 'search-results' ) ; // targets the <ul>
var fuse ; // holds our search engine
var search _ _focus = false ; // check to true to make visible by default
var results _available = false ; // did we get any search results?
var first _run = true ; // allow us to delay loading json data unless search activated
var first = search _results . firstChild ; // first child of search list
var last = search _results . lastChild ; // last child of search list
search _form . classList . remove ( 'noscript' ) ; // JavaScript is active
search _form . setAttribute ( 'data-focus' , search _ _focus ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The main keyboard event listener running the show
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
document . addEventListener ( 'keydown' , function ( e ) {
// console.log(event); // DEBUG
// Ctrl + / to show or hide Search
// if (event.metaKey && event.which === 191) {
if ( event . ctrlKey && event . which === 191 ) {
search _toggle _focus ( e ) ; // toggle visibility of search box
}
} ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The main keyboard event listener running the show
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
search _form . addEventListener ( 'keydown' , function ( e ) {
// Allow ESC (27) to close search box
if ( e . keyCode == 27 ) {
search _ _focus = true ; // make sure toggle removes focus
search _toggle _focus ( e ) ;
}
// DOWN (40) arrow
if ( e . keyCode == 40 ) {
if ( results _available ) {
e . preventDefault ( ) ; // stop window from scrolling
if ( document . activeElement == search _input ) { first . focus ( ) ; } // if the currently focused element is the main input --> focus the first <li>
else if ( document . activeElement == last ) { first . focus ( ) ; } // if we're at the bottom, loop to the start
// 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 ( e . keyCode == 38 ) {
if ( results _available ) {
e . preventDefault ( ) ; // stop window from scrolling
if ( document . activeElement == search _input ) { search _input . focus ( ) ; } // If we're in the input box, do nothing
else if ( document . activeElement == first ) { search _input . 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
}
}
// Use Enter (13) to move to the first result
2022-12-29 15:28:20 +00:00
if ( e . keyCode == 13 ) {
2022-12-29 14:55:43 +00:00
if ( results _available && document . activeElement == search _input ) {
e . preventDefault ( ) ; // stop form from being submitted
first . focus ( ) ;
}
}
// Use Backspace (8) to switch back to the search input
2022-12-29 15:28:20 +00:00
if ( e . keyCode == 8 ) {
2022-12-29 14:55:43 +00:00
if ( document . activeElement != search _input ) {
e . preventDefault ( ) ; // stop browser from going back in history
search _input . focus ( ) ;
}
}
} ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Load our json data and builds fuse . js search index
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
search _form . addEventListener ( 'focusin' , function ( e ) {
search _init ( ) ; // try to load the search index
} ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Make submit button toggle focus
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
search _form . addEventListener ( 'submit' , function ( e ) {
search _toggle _focus ( e ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Remove focus on blur
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
search _form . addEventListener ( 'focusout' , function ( e ) {
if ( e . relatedTarget === null ) {
search _toggle _focus ( e ) ;
}
else if ( e . relatedTarget . type === 'submit' ) {
e . stopPropagation ( ) ;
}
} ) ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Toggle focus UI of form
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
function search _toggle _focus ( e ) {
// console.log(e); // DEBUG
// order of operations is very important to keep focus where it should stay
if ( ! search _ _focus ) {
search _submit . value = '⨯ ' ;
search _form . setAttribute ( 'data-focus' , true ) ;
search _input . focus ( ) ; // move focus to search box
search _ _focus = true ;
}
else {
search _submit . value = '⌕' ;
search _form . setAttribute ( 'data-focus' , false ) ;
document . activeElement . blur ( ) ; // remove focus from search box
search _ _focus = false ;
}
}
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Fetch some json without jquery
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
function fetch _JSON ( 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 script
based on https : //stackoverflow.com/a/55451823
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
function load _script ( url ) {
return new Promise ( function ( resolve , reject ) {
let script = document . createElement ( "script" ) ;
script . onerror = reject ;
script . onload = resolve ;
if ( document . currentScript ) {
document . currentScript . parentNode . insertBefore ( script , document . currentScript ) ;
}
else {
document . head . appendChild ( script )
}
script . src = url ;
} ) ;
}
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Load our search index , only executed once
on first call of search box ( Ctrl + / )
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
function search _init ( ) {
if ( first _run ) {
2022-12-29 15:28:20 +00:00
load _script ( window . location . origin + '/js/fuse.js' ) . then ( ( ) => {
2022-12-29 14:55:43 +00:00
search _input . value = "" ; // reset default value
first _run = false ; // let's never do this again
2022-12-29 15:28:20 +00:00
fetch _JSON ( '/index.json' , function ( data ) {
2022-12-29 14:55:43 +00:00
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' ,
'date' ,
2022-12-29 15:28:20 +00:00
'description' ,
2022-12-29 14:55:43 +00:00
'tags'
]
} ;
fuse = new Fuse ( data , options ) ; // build the index from the json file
search _input . addEventListener ( 'keyup' , function ( e ) { // execute search as each character is typed
search _exec ( this . value ) ;
} ) ;
// console.log("index.json loaded"); // DEBUG
} ) ;
} ) . catch ( ( error ) => { console . log ( 'fixedsearch failed to load: ' + error ) ; } ) ;
}
}
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Using the index we loaded on Ctrl + / , r u n
a search query ( for "term" ) every time a letter is typed
in the search box
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
function search _exec ( term ) {
let results = fuse . search ( term ) ; // the actual query being run using fuse.js
let search _items = '' ; // our results bucket
if ( results . length === 0 ) { // no results based on what was typed into the input box
results _available = false ;
search _items = '' ;
} else { // build our html
for ( let item in results . slice ( 0 , 5 ) ) { // only show first 5 results
search _items = search _items +
` <li><a href=" ${ results [ item ] . item . permalink } " tabindex="0">
< span class = "title" > $ { results [ item ] . item . title } < / s p a n >
< span class = "date" > $ { results [ item ] . item . date } < / s p a n >
2022-12-29 15:28:20 +00:00
< span class = "description" > $ { results [ item ] . item . description } < / s p a n >
2022-12-29 14:55:43 +00:00
< span class = "tags" > $ { results [ item ] . item . tags . join ( ', ' ) } < / s p a n >
< / a > < / l i > ` ;
}
results _available = true ;
}
search _results . innerHTML = search _items ;
if ( results . length > 0 ) {
first = search _results . firstChild . firstElementChild ; // first result container — used for checking against keyboard up/down location
last = search _results . lastChild . firstElementChild ; // last result container — used for checking against keyboard up/down location
}
}
} ( ) ;
}