Lightweight JS Autocomplete & Autosuggest: Ajax & Local Data – Ajax Autocomplete Vanilla

Lightweight JS Autocomplete & Autosuggest: Ajax & Local Data – Ajax Autocomplete Vanilla
Lightweight JS Autocomplete & Autosuggest: Ajax & Local Data – Ajax Autocomplete Vanilla
Ajax Autocomplete is a vanilla JavaScript library that enables autocomplete and autosuggest functionality on any standard text input.

It supports both local array data and remote Ajax or JSONP endpoints, selectable per instance.

Features:

  • Pure ES6+ vanilla JavaScript.
  • Built-in response caching to reduce redundant network requests on repeated queries.
  • Bad-query prevention that tracks zero-result root queries and skips future requests with the same prefix.
  • Allows you to control how many characters the user must type before suggestions appear.
  • Keyboard navigation with arrow keys, Enter to confirm, Escape to dismiss, and Tab support with optional cursor retention.
  • Inline hint display for first-match type-ahead prediction.
  • Flexible container positioning: auto, top, or bottom orientation with scroll-aware recalculation on window resize.
  • Comma-separated or multi-token input fields that suggest values for each token.

Use Cases:

  • Country selection dropdown for checkout forms.
  • Dynamic site search bar that fetches product names from a database.
  • Tag input field that suggests available categories as the user types.
  • User lookup tool in an admin dashboard that queries an external API.

How to use it:

1. Download the library and load the following files in the document.

<!-- Core JavaScript Library -->
<script src="autocomplete.js"></script>

<!-- Optional. You can write your own CSS styles -->
<link rel="stylesheet" href="autocomplete.css" />

2. Create a standard text input in the HTML.

<input type="text" id="location-input" placeholder="Type a city name..." autocomplete="off" />

3. Define your suggestion dataset as an array of { value, data } objects.

var cities = [
    { value: 'New York',     data: 'NY' },
    { value: 'Los Angeles',  data: 'CA' },
    { value: 'Chicago',      data: 'IL' },
    { value: 'Houston',      data: 'TX' },
    { value: 'Philadelphia', data: 'PA' }
];

4. Attach autocomplete to the input using the local array

new Autocomplete(document.getElementById('location-input'), {
  lookup: cities,
  minChars: 1, 
  autoSelectFirst: true,
  onSelect: function (suggestion) {
    // suggestion.value holds the display text
    // suggestion.data holds your metadata (state code here)
    console.log('City: ' + suggestion.value + ' | Code: ' + suggestion.data);
  }
});

5. The library also works with plain string arrays.

var tags = ['JavaScript', 'TypeScript', 'CSS', 'HTML', 'WebAssembly'];
new Autocomplete(document.getElementById('location-input'), {
    lookup: tags,
    onSelect: function (suggestion) {
        console.log('Tag selected: ' + suggestion.value);
    }
});

6. Fetch suggestions from a server endpoint as the user types:

new Autocomplete(document.getElementById('location-input'), {
  serviceUrl: '/api/cities/suggest',
  paramName: 'q', 
  dataType: 'json',
  deferRequestBy: 250,
  minChars: 2,
  onSelect: function (suggestion) {
    document.getElementById('location-input').value = suggestion.value;
    console.log('Selected city code: ' + suggestion.data);
  },
  onSearchError: function (query, response, status, error) {
    console.error('Suggestion request failed for: "' + query + '"', error);
  }
});

Your server must return JSON in this format:

{
    "query": "Chi",
    "suggestions": [
        { "value": "Chicago",         "data": "IL" },
        { "value": "Chico",           "data": "CA" },
        { "value": "Chicopee",        "data": "MA" }
    ]
}

7. Use the Custom Lookup Function when your data source requires custom logic. For example, filtering an in-memory index, combining multiple endpoints, or wrapping a third-party SDK.

new Autocomplete(document.getElementById('location-input'), {
  lookup: function (query, done) {
    // Run any async operation here, then call done() with the results object
    fetch('/api/locations?term=' + encodeURIComponent(query))
      .then(function (res) { return res.json(); })
      .then(function (data) {
        done({
          suggestions: data.results.map(function (item) {
            return { value: item.displayName, data: item.locationId };
          })
        });
      })
      .catch(function () {
        done({ suggestions: [] }); // Return empty on error to avoid a broken state
      });
  },
  onSelect: function (suggestion) {
    console.log('Location ID: ' + suggestion.data);
  }
});

8. The library also has native JSONP support for working with cross-domain APIs that do not support CORS.

new Autocomplete(document.getElementById('location-input'), {
  serviceUrl: 'https://partner-api.example.com/cities/search',
  dataType: 'jsonp', // Triggers script-tag injection
  jsonpCallbackParam: 'cb', // The callback parameter the API expects
  jsonpTimeout: 6000, // Fire onSearchError if no response in 6 seconds
  onSelect: function (suggestion) {
    console.log('Received: ' + suggestion.value);
  },
  onSearchError: function (query, res, status, error) {
    console.warn('JSONP timeout or error for query: ' + query);
  }
});

9. Set groupBy to a property name on the suggestion’s data object. The library groups all suggestions that share the same value for that property under a single labeled header.

var sports = [
    { value: 'Brooklyn Nets',    data: { league: 'NBA', arena: 'Barclays Center' } },
    { value: 'New York Knicks',  data: { league: 'NBA', arena: 'Madison Square Garden' } },
    { value: 'New York Giants',  data: { league: 'NFL', arena: 'MetLife Stadium' } },
    { value: 'New York Rangers', data: { league: 'NHL', arena: 'Madison Square Garden' } }
];
new Autocomplete(document.getElementById('location-input'), {
  lookup: sports,
  groupBy: 'league', // Group rows by the 'league' property in the data object
  formatGroup: function (suggestion, category) {
    // Customize the group header HTML
    return '<div class="autocomplete-group"><strong>' + category + '</strong></div>';
  },
  onSelect: function (suggestion) {
    console.log(suggestion.value + ' plays at ' + suggestion.data.arena);
  }
});

10. Use paramName and transformResult together if your API uses a different query parameter name or returns data in a proprietary format.

new Autocomplete(document.getElementById('location-input'), {
  serviceUrl: '/api/legacy/locations',
  paramName: 'searchTerm', // Override the default 'query' parameter name
  transformResult: function (response) {
    // Parse the raw response and map it to the { suggestions: [] } format
    var parsed = typeof response === 'string' ? JSON.parse(response) : response;
    return {
      suggestions: parsed.locations.map(function (loc) {
        return { value: loc.cityName, data: loc.cityCode };
      })
    };
  },
  onSelect: function (suggestion) {
    console.log('Code: ' + suggestion.data + ' | Name: ' + suggestion.value);
  }
});

11. Create delimiter-Based token input:

new Autocomplete(document.getElementById('location-input'), {
  lookup: cities,
  delimiter: ',', 
  onSelect: function (suggestion) {
    // The library appends the selected value to the existing tokens automatically
    console.log('Added token: ' + suggestion.value);
  }
});

12. All configuration options and callback functions:

  • noCache (Boolean): Disables response caching when set to true. Default: false.
  • delimiter (String | RegExp): Splits the input value on this separator and uses the last segment as the active query. Default: none.
  • minChars (Number): Minimum character count the user must type before the suggestion lookup fires. Default: 1.
  • triggerSelectOnValidInput (Boolean): Fires the select event automatically when the input value is an exact, case-insensitive match for a single suggestion. Default: true.
  • preventBadQueries (Boolean): Tracks zero-result queries and blocks future requests for any query that starts with the same root string. Default: true.
  • autoSelectFirst (Boolean): Pre-highlights the first suggestion in the list on display. Default: false.
  • beforeRender (Function): function(container, suggestions) {} — called before the suggestion container becomes visible. Use it to manipulate the container DOM. container is the raw DOM element.
  • formatResult (Function): function(suggestion, currentValue) {} — custom rendering function for each suggestion row. Returns an HTML string.
  • formatGroup (Function): function(suggestion, category) {} — custom rendering function for a group header row. Returns an HTML string.
  • groupBy (String): A property name on the suggestion data object. Suggestions that share the same value for this property render under a single labeled header.
  • maxHeight (Number): Maximum height of the suggestion container in pixels. Default: 300.
  • width (Number | String): Suggestion container width. Pass a number for a fixed pixel width, 'flex' to match the widest suggestion, or 'auto' to match the input element width. Default: 'auto'.
  • zIndex (Number): CSS z-index applied to the suggestion container. Default: 9999.
  • appendTo (String | HTMLElement): The DOM element where the suggestion container appends. Accepts a CSS selector string or an HTML element reference. The target element must have position: absolute or position: relative set. Default: document.body.
  • forceFixPosition (Boolean): Forces automatic position recalculation when appendTo targets an element other than document.body. Default: false.
  • orientation (String): Vertical display direction for the suggestion container. Accepts 'auto', 'top', or 'bottom'. The 'auto' value places the container on whichever side has more viewport space. Default: 'bottom'.
  • preserveInput (Boolean): Keeps the raw input value unchanged while the user navigates suggestions with arrow keys. Default: false.
  • showNoSuggestionNotice (Boolean): Renders a notice inside the suggestion container when a query returns zero results. Default: false.
  • noSuggestionNotice (String | HTMLString | Element): Content for the no-results label. Accepts a plain string, an HTML string, or a DOM element. Default: 'No results'.
  • onInvalidateSelection (Function): function() {} — called when the user modifies the input value after a suggestion was selected. this refers to the input element.
  • tabDisabled (Boolean): When true, keeps the cursor in the input field after Tab selects a suggestion. Default: false.
  • lookup (Array | Function): The local suggestion source. Pass an array of strings, an array of { value, data } objects, or a function(query, done) {} callback for custom async logic.
  • lookupLimit (Number): Maximum number of local results the library displays. Default: no limit.
  • lookupFilter (Function): function(suggestion, query, queryLowerCase) {} — custom filter function for local lookups. The default performs a case-insensitive partial string match against suggestion.value.
  • serviceUrl (String | Function): The server endpoint URL, or a function that returns the URL string dynamically.
  • type (String): HTTP method for the Ajax request. Default: 'GET'.
  • dataType (String): Expected response format. Accepts 'text', 'json', or 'jsonp'. When 'jsonp' is set, the library injects a <script> tag. Default: 'text'.
  • paramName (String): The query parameter key the library sends to the server. Default: 'query'.
  • params (Object): Additional key-value pairs sent alongside the query on every request.
  • deferRequestBy (Number): Milliseconds to wait after a keystroke before firing the Ajax request. Use this to debounce rapid typing. Default: 0.
  • ajaxSettings (Object): Extra options passed to the native fetch() call. Accepts headers, credentials, mode, and cache. The library ignores this option for JSONP requests.
  • ignoreParams (Boolean): When true, the library omits params from the request entirely. Default: false.
  • jsonpCallbackParam (String): The JSONP callback parameter name the external API expects. Default: 'callback'.
  • jsonpTimeout (Number): Milliseconds before a pending JSONP request is considered timed out and onSearchError fires. Default: 10000.
  • onSearchStart (Function): function(params) {} — called before each Ajax request. this refers to the input element.
  • onHint (Function): function(hint) {} — called to populate the input with a type-ahead hint based on the first matching suggestion. this refers to the input element.
  • onSearchComplete (Function): function(query, suggestions) {} — called after the Ajax response is processed. suggestions is the result array. this refers to the input element.
  • transformResult (Function): function(response, originalQuery) {} — transforms the raw server response into the { suggestions: [] } format the library expects before caching or rendering.
  • onSelect (Function): function(suggestion) {} — called when the user confirms a suggestion. this refers to the input element.
  • onSearchError (Function): function(query, response, textStatus, error) {} — called when an Ajax or JSONP request fails. this refers to the input element.
  • onHide (Function): function(container) {} — called just before the suggestion container hides. container is the raw DOM element.

13. API methods.

// Retrieve a live instance from a DOM element reference
var ac = Autocomplete.getInstance(document.getElementById('location-input'));

// Update any option on a running instance without reconstructing it
ac.setOptions({ maxHeight: 400, deferRequestBy: 300 });

// Clear both the response cache and the current suggestion list
ac.clear();

// Clear only the cached responses; leave current suggestions in place
ac.clearCache();

// Temporarily suspend autocomplete behavior (useful during form validation)
ac.disable();

// Re-activate autocomplete after it was disabled
ac.enable();

// Programmatically close the suggestion container
ac.hide();

// Destroy the instance: detach all event listeners, remove the container from the DOM,
// and delete the entry from the internal WeakMap to free memory
ac.dispose();

// You can also chain initialization and a method call by saving the constructor return value:
// Create an instance and immediately call a method on it
var ac = new Autocomplete(document.getElementById('location-input'), { lookup: cities });
ac.setOptions({ autoSelectFirst: true });

14. The library generates the following class names that you can customize your autocomplete dropdown using CSS styles.

<div class="autocomplete-suggestions">
  <div class="autocomplete-group"><strong>NBA</strong></div>
  <div class="autocomplete-suggestion autocomplete-selected">Brooklyn Nets</div>
  <div class="autocomplete-suggestion">New York Knicks</div>
</div>
/* Outer suggestion container */
.autocomplete-suggestions {
  border: 1px solid #ccc;
  background: #fff;
  overflow-y: auto;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  border-radius: 4px;
}

/* Each suggestion row */
.autocomplete-suggestion {
  padding: 8px 12px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer; /* Required for tap events on Mobile Safari */
}

/* Currently highlighted row */
.autocomplete-selected {
  background: #eef2ff;
  color: #3333cc;
}

/* Bolded matched characters injected by the default formatResult */
.autocomplete-suggestions strong {
  font-weight: 600;
  color: #3366ff;
}

/* Group header row */
.autocomplete-group {
  padding: 4px 12px;
}

.autocomplete-group strong {
  display: block;
  border-bottom: 1px solid #e0e0e0;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #999;
}

Alternatives:

FAQs:

Q: Why does the suggestion container appear behind modals or fixed headers on my page?
A: The default zIndex is 9999. If your page contains elements with a higher stacking context, pass a higher value in the options: new Autocomplete(el, { zIndex: 10001 }).

Q: How do I connect this library to a REST API that uses a different parameter name and returns data in a custom schema?

A: Use paramName to set the query key the server expects, and use transformResult to remap the response body to { suggestions: [{ value, data }] }.

Q: The library fires too many Ajax requests when the user types quickly. How do I reduce the load?
A: Set deferRequestBy to a value between 150 and 300 milliseconds. The library debounces the request by that interval from the last keystroke.

Q: Can I attach different configurations to multiple input fields on the same page?
A: Yes. Each new Autocomplete() call creates an independent instance stored against that specific input element’s reference.

The post Lightweight JS Autocomplete & Autosuggest: Ajax & Local Data – Ajax Autocomplete Vanilla appeared first on CSS Script.


Discover more from RSS Feeds Cloud

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Discover more from RSS Feeds Cloud

Subscribe now to keep reading and get access to the full archive.

Continue reading