
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 totrue. 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 theselectevent 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.containeris 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 suggestiondataobject. 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): CSSz-indexapplied 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 haveposition: absoluteorposition: relativeset. Default:document.body.forceFixPosition(Boolean): Forces automatic position recalculation whenappendTotargets an element other thandocument.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.thisrefers to the input element.tabDisabled(Boolean): Whentrue, 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 afunction(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 againstsuggestion.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 nativefetch()call. Acceptsheaders,credentials,mode, andcache. The library ignores this option for JSONP requests.ignoreParams(Boolean): Whentrue, the library omitsparamsfrom 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 andonSearchErrorfires. Default:10000.onSearchStart(Function):function(params) {}— called before each Ajax request.thisrefers to the input element.onHint(Function):function(hint) {}— called to populate the input with a type-ahead hint based on the first matching suggestion.thisrefers to the input element.onSearchComplete(Function):function(query, suggestions) {}— called after the Ajax response is processed.suggestionsis the result array.thisrefers 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.thisrefers to the input element.onSearchError(Function):function(query, response, textStatus, error) {}— called when an Ajax or JSONP request fails.thisrefers to the input element.onHide(Function):function(container) {}— called just before the suggestion container hides.containeris 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.
