@ -1,4 +1,9 @@
const handleUserInput = searchBar => {
let searchInput ;
let currentFocus ;
let originalSearch ;
let autocompleteResults ;
const handleUserInput = ( ) => {
let xhrRequest = new XMLHttpRequest ( ) ;
xhrRequest . open ( "POST" , "autocomplete" ) ;
xhrRequest . setRequestHeader ( "Content-type" , "application/x-www-form-urlencoded" ) ;
@ -9,118 +14,119 @@ const handleUserInput = searchBar => {
}
// Fill autocomplete with fetched results
let autocompleteResults = JSON . parse ( xhrRequest . responseText ) ;
autocomplete( searchBar , autocompleteResults [ 1 ] ) ;
autocompleteResults = JSON . parse ( xhrRequest . responseText ) [ 1 ] ;
updateAutocompleteList( ) ;
} ;
xhrRequest . send ( 'q=' + search Bar . value ) ;
xhrRequest . send ( 'q=' + search Input . value ) ;
} ;
const autocomplete = ( searchInput , autocompleteResults ) => {
let currentFocus ;
let originalSearch ;
searchInput . addEventListener ( "input" , function ( ) {
let autocompleteList , autocompleteItem , i , val = this . value ;
closeAllLists ( ) ;
if ( ! val || ! autocompleteResults ) {
return false ;
const closeAllLists = el => {
// Close all autocomplete suggestions
let suggestions = document . getElementsByClassName ( "autocomplete-items" ) ;
for ( let i = 0 ; i < suggestions . length ; i ++ ) {
if ( el !== suggestions [ i ] && el !== searchInput ) {
suggestions [ i ] . parentNode . removeChild ( suggestions [ i ] ) ;
}
}
} ;
currentFocus = - 1 ;
autocompleteList = document . createElement ( "div" ) ;
autocompleteList . setAttribute ( "id" , this . id + "-autocomplete-list" ) ;
autocompleteList . setAttribute ( "class" , "autocomplete-items" ) ;
this . parentNode . appendChild ( autocompleteList ) ;
for ( i = 0 ; i < autocompleteResults . length ; i ++ ) {
if ( autocompleteResults [ i ] . substr ( 0 , val . length ) . toUpperCase ( ) === val . toUpperCase ( ) ) {
autocompleteItem = document . createElement ( "div" ) ;
autocompleteItem . innerHTML = "<strong>" + autocompleteResults [ i ] . substr ( 0 , val . length ) + "</strong>" ;
autocompleteItem . innerHTML += autocompleteResults [ i ] . substr ( val . length ) ;
autocompleteItem . innerHTML += "<input type=\"hidden\" value=\"" + autocompleteResults [ i ] + "\">" ;
autocompleteItem . addEventListener ( "click" , function ( ) {
searchInput . value = this . getElementsByTagName ( "input" ) [ 0 ] . value ;
closeAllLists ( ) ;
document . getElementById ( "search-form" ) . submit ( ) ;
} ) ;
autocompleteList . appendChild ( autocompleteItem ) ;
}
}
} ) ;
const removeActive = suggestion => {
// Remove "autocomplete-active" class from previously active suggestion
for ( let i = 0 ; i < suggestion . length ; i ++ ) {
suggestion [ i ] . classList . remove ( "autocomplete-active" ) ;
}
} ;
searchInput . addEventListener ( "keydown" , function ( e ) {
let suggestion = document . getElementById ( this . id + "-autocomplete-list" ) ;
if ( suggestion ) suggestion = suggestion . getElementsByTagName ( "div" ) ;
if ( e . keyCode === 40 ) { // down
e . preventDefault ( ) ;
currentFocus ++ ;
addActive ( suggestion ) ;
} else if ( e . keyCode === 38 ) { //up
e . preventDefault ( ) ;
currentFocus -- ;
addActive ( suggestion ) ;
} else if ( e . keyCode === 13 ) { // enter
e . preventDefault ( ) ;
if ( currentFocus > - 1 ) {
if ( suggestion ) suggestion [ currentFocus ] . click ( ) ;
}
const addActive = ( suggestion ) => {
// Handle navigation outside of suggestion list
if ( ! suggestion || ! suggestion [ currentFocus ] ) {
if ( currentFocus >= suggestion . length ) {
// Move selection back to the beginning
currentFocus = 0 ;
} else if ( currentFocus < 0 ) {
// Retrieve original search and remove active suggestion selection
currentFocus = - 1 ;
searchInput . value = originalSearch ;
removeActive ( suggestion ) ;
return ;
} else {
originalSearch = document . getElementById ( "search-bar" ) . value ;
}
} ) ;
const addActive = suggestion => {
let searchBar = document . getElementById ( "search-bar" ) ;
// Handle navigation outside of suggestion list
if ( ! suggestion || ! suggestion [ currentFocus ] ) {
if ( currentFocus >= suggestion . length ) {
// Move selection back to the beginning
currentFocus = 0 ;
} else if ( currentFocus < 0 ) {
// Retrieve original search and remove active suggestion selection
currentFocus = - 1 ;
searchBar . value = originalSearch ;
removeActive ( suggestion ) ;
return ;
} else {
return ;
}
return ;
}
}
removeActive ( suggestion ) ;
suggestion [ currentFocus ] . classList . add ( "autocomplete-active" ) ;
removeActive ( suggestion ) ;
suggestion [ currentFocus ] . classList . add ( "autocomplete-active" ) ;
// Autofill search bar with suggestion content (minus the "bang name" if using a bang operator)
let searchContent = suggestion [ currentFocus ] . textContent ;
if ( searchContent . indexOf ( '(' ) > 0 ) {
searchBar . value = searchContent . substring ( 0 , searchContent . indexOf ( '(' ) ) ;
} else {
searchBar . value = searchContent ;
}
// Autofill search bar with suggestion content (minus the "bang name" if using a bang operator)
let searchContent = suggestion [ currentFocus ] . textContent ;
if ( searchContent . indexOf ( '(' ) > 0 ) {
searchInput . value = searchContent . substring ( 0 , searchContent . indexOf ( '(' ) ) ;
} else {
searchInput . value = searchContent ;
}
searchBar . focus ( ) ;
} ;
searchInput . focus ( ) ;
} ;
const removeActive = suggestion => {
for ( let i = 0 ; i < suggestion . length ; i ++ ) {
suggestion [ i ] . classList . remove ( "autocomplete-active" ) ;
const autocompleteInput = ( e ) => {
// Handle navigation between autocomplete suggestions
let suggestion = document . getElementById ( this . id + "-autocomplete-list" ) ;
if ( suggestion ) suggestion = suggestion . getElementsByTagName ( "div" ) ;
if ( e . keyCode === 40 ) { // down
e . preventDefault ( ) ;
currentFocus ++ ;
addActive ( suggestion ) ;
} else if ( e . keyCode === 38 ) { //up
e . preventDefault ( ) ;
currentFocus -- ;
addActive ( suggestion ) ;
} else if ( e . keyCode === 13 ) { // enter
e . preventDefault ( ) ;
if ( currentFocus > - 1 ) {
if ( suggestion ) suggestion [ currentFocus ] . click ( ) ;
}
} ;
} else {
originalSearch = searchInput . value ;
}
} ;
const closeAllLists = el => {
let suggestions = document . getElementsByClassName ( "autocomplete-items" ) ;
for ( let i = 0 ; i < suggestions . length ; i ++ ) {
if ( el !== suggestions [ i ] && el !== searchInput ) {
suggestions [ i ] . parentNode . removeChild ( suggestions [ i ] ) ;
}
const updateAutocompleteList = ( ) => {
let autocompleteList , autocompleteItem , i ;
let val = originalSearch ;
closeAllLists ( ) ;
if ( ! val || ! autocompleteResults ) {
return false ;
}
currentFocus = - 1 ;
autocompleteList = document . createElement ( "div" ) ;
autocompleteList . setAttribute ( "id" , this . id + "-autocomplete-list" ) ;
autocompleteList . setAttribute ( "class" , "autocomplete-items" ) ;
searchInput . parentNode . appendChild ( autocompleteList ) ;
for ( i = 0 ; i < autocompleteResults . length ; i ++ ) {
if ( autocompleteResults [ i ] . substr ( 0 , val . length ) . toUpperCase ( ) === val . toUpperCase ( ) ) {
autocompleteItem = document . createElement ( "div" ) ;
autocompleteItem . innerHTML = "<strong>" + autocompleteResults [ i ] . substr ( 0 , val . length ) + "</strong>" ;
autocompleteItem . innerHTML += autocompleteResults [ i ] . substr ( val . length ) ;
autocompleteItem . innerHTML += "<input type=\"hidden\" value=\"" + autocompleteResults [ i ] + "\">" ;
autocompleteItem . addEventListener ( "click" , function ( ) {
searchInput . value = this . getElementsByTagName ( "input" ) [ 0 ] . value ;
closeAllLists ( ) ;
document . getElementById ( "search-form" ) . submit ( ) ;
} ) ;
autocompleteList . appendChild ( autocompleteItem ) ;
}
} ;
}
} ;
document . addEventListener ( "DOMContentLoaded" , function ( ) {
searchInput = document . getElementById ( "search-bar" ) ;
searchInput . addEventListener ( "keydown" , ( event ) => autocompleteInput ( event ) ) ;
// Close lists and search when user selects a suggestion
document . addEventListener ( "click" , function ( e ) {
closeAllLists ( e . target ) ;
} ) ;
} ;
} ) ;