Usage
nebula.autocompleteSearch(element, types)
Parameters
element
(Required) (Object (or String)) The jQuery object (or selector) of the search input field
Default: None
types
(Optional) (String) A comma-separated string of post types to include
Default: 'any'
Examples
Include only posts and pages in search.
nebula.loadJS(nebula.site.resources.scripts.nebula_jquery_ui, 'jquery-ui').then(function(){ //Ensure jQuery-UI JS is loaded
jQuery('#example-input').on('keyup paste', function(){ //Add the listener
nebula.autocompleteSearch(jQuery(this), 'post,page');
});
});
nebula.loadCSS(nebula.site.resources.styles.nebula_jquery_ui); //Load the jQuery-UI CSS too
Search all post types by simply using the WordPress core "s" class.
<input class="s" type="text" placeholder="Search this site..." />
Customize the post types in an HTML form without needing to re-write any JavaScript
<input class="search" type="text" data-types="post,page" />
Additional Notes
For an easier implementation, you could simply add the class s to the search input. See autocompleteSearchListeners().
On keypress or paste, this function triggers nebula_autocomplete_search_start.
On autocomplete success, nebula_autocomplete_search_success is triggered.
When search results are available, nebula_autocomplete_search_results is triggered, otherwise nebula_autocomplete_search_no_results is triggered if no results are available.
On AJAX error, this function triggers nebula_autocomplete_search_error.
Source File
Located in /assets/js/modules/search.js on line 156.
No Hooks
This function does not have any filters or actions available. Request one?nebula.autocompleteSearch = async function($element, types = ''){
await nebula.yield();
if ( typeof $element === 'string' ){
$element = jQuery($element);
}
if ( types && Array.isArray(types) ){
types = types.join(','); //Convert an array to to a comma-separated string
}
nebula.dom.document.trigger('nebula_autocomplete_search_start', $element);
nebula.timer('(Nebula) Autocomplete Search', 'start');
nebula.timer('(Nebula) Autocomplete Response', 'start');
if ( $element.val().trim().length ){
if ( $element.val().trim().length >= 2 ){ //This checks the length for animation but the minlength (below) handles it for autocomplete
//Add "searching" class for custom Nebula styled forms
$element.closest('form').addClass('searching');
setTimeout(function(){
$element.closest('form').removeClass('searching');
}, 10_000);
//Swap magnifying glass on Bootstrap input-group
$element.closest('.input-group, .nebula-input-group').find('.fa-magnifying-glass').removeClass('fa-magnifying-glass').addClass('fa-spin fa-spinner');
} else {
$element.closest('form').removeClass('searching');
$element.closest('.input-group, .nebula-input-group').find('.fa-spin').removeClass('fa-spin fa-spinner').addClass('fa-magnifying-glass');
}
let typesQuery = '';
if ( types ){
typesQuery = '&types=' + types;
}
if ( typeof $element.autocomplete !== 'function' ){
nebula.help('nebula.autocompleteSearch requires jQuery UI. Load that library before calling this function', '/functions/autocompletesearch/');
return false;
}
let minLength = 3;
if ( $element.attr('data-min-length') ){
minLength = $element.attr('data-min-length');
}
$element.autocomplete({ //jQuery UI dependent
position: {
my: 'left top-2px',
at: 'left bottom',
collision: 'flip',
},
source: async function(request, sourceResponse){
await nebula.yield();
if ( !request?.term ){ //If the search term is empty, ignore it
return false;
}
let searchResults = nebula.memoize('get', 'autocomplete search (' + request.term.toLowerCase() + ') [' + typesQuery + ']'); //Try to get from stored memory first
//If we didn't find the search from stored memory, do an actual search
if ( !searchResults ){
//Fetch an actual search query from the WP JSON API
var fetchResponse = await fetch(nebula.site.home_url + '/wp-json/nebula/v2/autocomplete_search?term=' + request.term + typesQuery, {
priority: 'high'
}).then(function(fetchResponse){
return fetchResponse.json();
}).then(function(fetchData){
searchResults = nebula.memoize('set', 'autocomplete search (' + request.term.toLowerCase() + ') [' + typesQuery + ']', fetchData); //Add to stored memory
}).catch(function(error){
nebula.dom.document.trigger('nebula_autocomplete_search_error', request.term);
nebula.debounce(function(){
gtag('event', 'exception', {
message: '(JS) Autocomplete AJAX error: ' + error,
fatal: false
});
nebula.crm('event', 'Autocomplete Search AJAX Error');
}, 1500, 'autocomplete error buffer');
$element.closest('form').removeClass('searching');
$element.closest('.input-group, .nebula-input-group').find('.fa-spin').removeClass('fa-spin fa-spinner').addClass('fa-magnifying-glass');
});
}
nebula.dom.document.trigger('nebula_autocomplete_search_success', searchResults);
var noSearchResults = ' (No Results)'; //Prep the string
if ( searchResults ){
nebula.dom.document.trigger('nebula_autocomplete_search_results', searchResults);
nebula.prefetch(searchResults[0].link);
jQuery.each(searchResults, function(index, value){
value.label = value.label.replaceAll(/&/g, '\&');
});
noSearchResults = '';
} else {
nebula.dom.document.trigger('nebula_autocomplete_search_no_results');
}
nebula.debounce(function(){
let thisEvent = {
event_name: 'autocomplete_search',
event_category: 'Internal Search',
event_action: 'Autocomplete Search' + noSearchResults,
event_label: request.term.toLowerCase(),
link_text: request.term.toLowerCase(),
search_term: request.term.toLowerCase(),
no_search_results: ( noSearchResults )? true : false,
};
nebula.dom.document.trigger('nebula_event', thisEvent);
gtag('event', thisEvent.event_name, nebula.gaEventObject(thisEvent));
nebula.fbq('track', 'Search', {search_string: thisEvent.term});
nebula.clarity('set', thisEvent.category, thisEvent.term);
nebula.crm('identify', {internal_search: thisEvent.term});
}, 1500, 'autocomplete success buffer');
gtag('event', 'timing_complete', {
name: 'Server Response',
value: Math.round(nebula.timer('(Nebula) Autocomplete Search', 'lap')),
event_category: 'Autocomplete Search',
event_label: 'Each search until server results',
non_interaction: true
});
sourceResponse(searchResults); //Respond to the jQuery UI Autocomplete now
$element.closest('form').removeClass('searching').addClass('autocompleted');
$element.closest('.input-group, .nebula-input-group').find('.fa-spin').removeClass('fa-spin fa-spinner').addClass('fa-magnifying-glass');
},
focus: function(event, ui){
event.preventDefault(); //Prevent input value from changing.
},
select: function(event, ui){
let thisEvent = {
event_name: 'autocomplete_search_result_click',
event_category: 'Internal Search',
event_action: 'Autocomplete Click',
event_label: ui.item.label,
link_text: ui.item.label,
search_term: ui.item.label,
external: ( typeof ui.item.external !== 'undefined' )? true : false,
};
nebula.dom.document.trigger('nebula_autocomplete_search_selected', thisEvent.ui);
nebula.dom.document.trigger('nebula_event', thisEvent);
gtag('event', thisEvent.event_name, nebula.gaEventObject(thisEvent));
gtag('event', 'timing_complete', {
name: 'Until Navigation',
value: Math.round(nebula.timer('(Nebula) Autocomplete Search', 'end')),
event_category: 'Autocomplete Search',
event_label: 'From first initial search until navigation',
non_interaction: true
});
if ( thisEvent.external ){
window.open(ui.item.link, '_blank');
} else {
window.location.href = ui.item.link;
}
},
open: function(){
$element.closest('form').addClass('autocompleted');
let heroAutoCompleteDropdown = jQuery('.form-identifier-nebula-hero-search');
heroAutoCompleteDropdown.css('max-width', $element.outerWidth());
},
close: function(){
$element.closest('form').removeClass('autocompleted');
},
minLength: minLength, //Require at least 3 characters (unless overridden by an attribute)
}).data('ui-autocomplete')._renderItem = function(ul, item){
let thisSimilarity = ( typeof item.similarity !== 'undefined' )? item.similarity.toFixed(1) + '% Match' : '';
let listItem = jQuery("<li class='" + item.classes + "' title='" + thisSimilarity + "'></li>").data("item.autocomplete", item).append("<a href='" + item.link + "'> " + item.label.replaceAll(/\\/g, '') + "</a>").appendTo(ul);
return listItem;
};
let thisFormIdentifier = $element.closest('form').attr('id') || $element.closest('form').attr('name') || $element.closest('form').attr('class');
$element.autocomplete('widget').addClass('form-identifier-' + thisFormIdentifier);
}
};
Override
To override or disable this JavaScript function, simply redeclare it with the exact same function name. Remember: Some functionality is conditionally loaded via dynamic imports, so if your function is not overriding properly, try listening for a DOM event (described below).
For non-module import functions:
nebula.autocompleteSearch = function(element, types){
//Write your own code here, leave it blank, or return false.
}
For dynamically imported module function overrides:
jQuery(window).on('load', function(){
nebula.autocompleteSearch = function(element, types){
//Write your own code here, leave it blank, or return false.
}
});
Custom Nebula DOM events do also exist, so you could also try the following if the Window Load listener does not work:
jQuery(document).on('nebula_module_loaded', function(module){
//Note that the module variable is also available to know which module specifically was imported
if ( typeof nebula.autocompleteSearch === 'function' ){
nebula.autocompleteSearch = function(element, types){
//Write your own code here, leave it blank, or return false.
}
}
});