Skip to Content
Menu

autocompleteSearch()

Run an AJAX autocomplete search using jQuery UI.

JavaScript February 11, 2021

Usage

JavaScript
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'

Request or provide clarification »

Examples

Include only posts and pages in search.

JavaScript
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.

HTML
<input class="s" type="text" placeholder="Search this site..." />

Customize the post types in an HTML form without needing to re-write any JavaScript

HTML
<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.

Was this page helpful? Yes No


    A feedback message is required to submit this form.


    Please check that you have entered a valid email address.

    Enter your email address if you would like a response.

    Thank you for your feedback!

    Source File

    Located in /assets/js/modules/search.js on line 145.

    No Hooks

    This function does not have any filters or actions available. Request one?
    JavaScript
    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();
    
                    let searchResults = nebula.memoize('get', 'autocomplete search (' + request.term.toLowerCase() + ') [' + typesQuery + ']'); //Try 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(/&#038;/g, '\&');
                        });
    
                        noSearchResults = '';
                    } else {
                        nebula.dom.document.trigger('nebula_autocomplete_search_no_results');
                    }
    
                    nebula.debounce(function(){
                        let thisEvent = {
                            event_name: 'search',
                            event_category: 'Internal Search',
                            event_action: 'Autocomplete Search' + noSearchResults,
                            request: request,
                            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',
                    });
    
                    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: 'select_content',
                        event_category: 'Internal Search',
                        event_action: 'Autocomplete Click',
                        ui: ui,
                        event_label: 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',
                    });
    
                    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).

    JavaScript

    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.
            }
    	}
    });