Quick Start
No jQuery. No build step. Drop two files and go.
1. Include files
<!-- CSS --> <link rel="stylesheet" href="/path/to/jquery.flexdatalist.css"> <!-- JS (no jQuery needed) --> <script src="/path/to/flexdatalist.js"></script>
2. HTML — auto-init
<input type="text" class="flexdatalist" data-data="/api/items.json" data-search-in="name" data-min-length="1" name="item">
Any input.flexdatalist is initialised automatically on DOMContentLoaded.
3. JavaScript — programmatic init
// init() is async — resolves after initial data loading is done const [fd] = await Flexdatalist.init('#city', { url: '/api/cities', searchIn: ['name', 'zip'], valueProperty: 'id', textProperty: '{name}, {zip}', minLength: 2, }); // Chainable API fd.setValue('42') .on('select:flexdatalist', e => console.log(e.detail)); // Get the stored value at any time console.log(fd.getValue()); // When constructing directly, use .ready instead const fd2 = new Flexdatalist(document.querySelector('#city'), opts); await fd2.ready; // resolves once initial data is loaded
input.flexdatalist elements are initialised automatically unless they carry the class autodiscover-disabled. Options are read from data-* attributes (camelCase → kebab-case).
jquery.flexdatalist.css works without modification.
Data Sources
Three ways to provide data.
Static Array
Pass a JS array via the data option.
Flexdatalist.init('#el', { data: [ {id: 1, label: 'Apple'}, {id: 2, label: 'Banana'}, ] });
Static JSON File
Point data to a URL — loaded once and cached.
<input class="flexdatalist" data-data="items.json" data-search-in="label" name="item">
Remote URL recommended for large sets
Use url — queried on every keystroke.
<input class="flexdatalist" data-url="/api/search" data-search-in="name" name="item">
Server receives: keyword, contain, selected, load, relatives.
[{...}] or wrap it:
{
"results": [ { "id": 1, "name": "Paris" }, ... ],
"options": { "url": "/api/next-endpoint" } // optional — override options from server
}
Options
Set via JS constructor object or data-* attributes (camelCase → kebab-case). Options can also be changed at any time via fd.setOption(name, value).
| Option | Type | Default | Description |
|---|---|---|---|
| Data | |||
| data | Array|string | [] |
Static data array or URL string to a JSON file (loaded once, cached). |
| url | string|null | null |
Remote URL queried on every keystroke. Server-side filtering recommended for large datasets. |
| params | Object|Function | {} |
Extra query-string / body parameters sent with every remote request. May be a function (keyword) => Object. |
| resultsProperty | string | 'results' |
Key in the remote JSON response that holds the results array. |
| keywordParamName | string | 'keyword' |
Query-string parameter name that carries the typed keyword. |
| searchContainParamName | string | 'contain' |
Query-string parameter name that carries the searchContain flag. |
| requestType | string | 'get' |
HTTP method for remote requests: 'get' or 'post'. |
| requestContentType | string | 'x-www-form-urlencoded' |
Content-type for POST bodies: 'x-www-form-urlencoded' or 'json'. |
| requestHeaders new | Object|null | null |
Extra HTTP headers merged into every request. Useful for Authorization tokens. |
| Search | |||
| searchIn | string[] | ['label'] |
Properties searched when filtering results locally. |
| minLength | number | 3 |
Minimum characters before triggering search. Set 0 to show all results on focus. |
| searchContain | boolean | false |
Match keyword anywhere in the string (not just at the start). |
| searchEqual new | boolean | false |
Require an exact full-string match. |
| searchByWord | boolean | false |
Split keyword on spaces and match all words independently. |
| searchDisabled | boolean | false |
Skip local filtering entirely (rely entirely on server-side search). |
| searchDelay | number | 300 |
Debounce delay in ms before running a search after each keystroke. |
| normalizeString | Function|null | null |
Custom string normalizer called before comparison. Signature: (string) => string. Useful for accent-insensitive search (e.g. with latinize). |
| redoSearchOnFocus | boolean | true |
Re-trigger the search when the alias input re-gains focus. |
| Display | |||
| textProperty | string|null | null |
Property (or {placeholder} pattern) shown in the alias input after selection. Defaults to first entry of searchIn. |
| valueProperty | string|string[]|null | null |
Property stored as the actual submitted value. Use '*' to store the entire matched object as JSON. |
| visibleProperties | string[] | [] |
Properties rendered inside each result <li>. Supports {placeholder} patterns. Defaults to searchIn. |
| iconProperty | string | 'thumb' |
Property containing an image URL rendered as <img> in results. |
| groupBy | string|false | false |
Group results by a property value (e.g. 'continent'). |
| maxShownResults | number | 100 |
Maximum number of results rendered in the dropdown (0 = unlimited). |
| noResultsText | string | 'No results found for "{keyword}"' |
Message shown when no results match. Set to empty string to suppress. |
| resultsLoader | string|null | null |
URL of a loading spinner image shown while fetching remote results. |
| focusFirstResult | boolean | false |
Automatically activate (highlight) the first result item. |
| Values | |||
| multiple | boolean|null | null |
Allow multiple tags. When null, inferred from the multiple HTML attribute. |
| selectionRequired | boolean | false |
Require the user to select a result from the dropdown (disables free-text entry). |
| limitOfValues | number | 0 |
Maximum number of tags in multiple mode (0 = unlimited). |
| valuesSeparator | string | ',' |
Separator used when serialising multiple values into a string. |
| allowDuplicateValues | boolean | false |
Allow adding the same value more than once. |
| removeOnBackspace | boolean | true |
Pressing Backspace on an empty alias marks then removes the last tag. |
| toggleSelected | boolean | false |
Clicking a selected tag in multiple mode toggles its disabled state. |
| collapseAfterN new | number|false | 50 |
Collapse multiple-value tags after this many items. Set false to disable collapsing. |
| collapsedValuesText new | string | '{count} More' |
Label for the collapse toggle. {count} is replaced with the hidden count. |
| showAddNewItem new | boolean | false |
Show an "Add new item" option at the bottom when no results match. |
| addNewItemText new | string | 'No results found for "{keyword}". Click to add it.' |
Text of the "Add new item" option. {keyword} is replaced. |
| Relatives | |||
| relatives | string|NodeList|null | null |
CSS selector or NodeList of inputs whose values are sent as relatives[name] with every request. |
| chainedRelatives | boolean | false |
Disable this input until all relatives have a value. |
| State & Cache | |||
| disabled | boolean|null | null |
Start disabled. When null, inferred from the disabled HTML attribute. |
| cache | boolean | true |
Cache remote results in localStorage. |
| cacheLifetime | number | 60 |
Cache lifetime in seconds. |
| debug | boolean | true |
Log warnings to the browser console. |
API
All instance methods are chainable (return this) unless they return a value.
Static Methods
| Signature | Returns | Description |
|---|---|---|
| Flexdatalist.init(selector, options) | Promise<Flexdatalist[]> | Initialise on one or many elements. Accepts a CSS selector string, HTMLElement, or NodeList. Resolves after all instances have finished loading their initial data. |
| Flexdatalist.getInstance(el) | Flexdatalist|null | Return the instance attached to a given element, or null. |
| instance.ready | Promise<Flexdatalist> | Promise that resolves with the instance once initial data loading is complete. Useful when constructing instances directly with new Flexdatalist(el, opts). |
Options
| Signature | Returns | Description |
|---|---|---|
| fd.getOption(name) | any | Return the current value of an option. |
| fd.setOption(name, value) | this | Update an option at runtime. Chainable. |
Value
| Signature | Returns | Description |
|---|---|---|
| fd.getValue() | string|Object|Array | Return the stored value as a string, parsed object, or array (multiple mode). |
| fd.getText(format?) | string|string[] | Return the user-facing display text shown in the alias input. Single mode always returns a string. Multiple mode: 'array' (default) returns a string[]; 'string' joins with the configured valuesSeparator; any other string is used as a custom join separator (e.g. ' | '). |
| fd.setValue(val) | this | Replace the current value. Triggers a data load to resolve display text. |
| fd.addValue(val) | this | Add a value in multiple mode. |
| fd.removeValue(val) | this | Remove a value in multiple mode. |
| fd.toggleValue(val) | this | Add the value if not present, remove if already present. |
| fd.clear() | this | Clear all values and reset the alias input. |
State
| Signature | Returns | Description |
|---|---|---|
| fd.disable() | this | Disable the input. |
| fd.enable() | this | Enable the input. |
| fd.readonly(state?) | boolean|this | Called with no argument: returns current readonly state. Called with boolean: sets readonly state (chainable). |
| fd.isDisabled() | boolean | Return true if currently disabled. |
| fd.isReadonly() | boolean | Return true if currently readonly. |
Search & Results
| Signature | Returns | Description |
|---|---|---|
| fd.search(keyword) | this | Programmatically trigger a search with the given keyword. |
| fd.closeResults() | this | Close the results dropdown. |
Event Binding
| Signature | Returns | Description |
|---|---|---|
| fd.on(eventName, handler) | this | Attach an event listener on the underlying <input> element. Chainable. |
| fd.off(eventName, handler) | this | Remove a previously attached event listener. Chainable. |
Lifecycle
| Signature | Returns | Description |
|---|---|---|
| fd.destroy(clear = false) | void | Remove all DOM additions, unbind all listeners, and unregister the instance. Pass true to also clear the original element's value. |
Chaining Example
const [fd] = await Flexdatalist.init('#tags', { data: myArray, multiple: true, selectionRequired: true, limitOfValues: 5, }); fd.setValue('1,2,3') // pre-select three values .setOption('limitOfValues', 10) // change an option at runtime .on('change:flexdatalist', e => { console.log('changed', e.detail); // { value, text } }) .on('select:flexdatalist', e => { console.log('selected item', e.detail); // the full item object }); // Later… fd.addValue('4').disable(); // Or via native addEventListener document.querySelector('#tags') .addEventListener('select:flexdatalist', e => console.log(e.detail));
Events
All events are dispatched as CustomEvent
on the original <input> element.
The payload is available at e.detail.
Listen with fd.on(eventName, handler) or the native
el.addEventListener(eventName, handler).
| Event Name | e.detail | Description |
|---|---|---|
| change:flexdatalist | {value, text} |
Fired every time the stored value changes. value is the submitted value, text is the displayed text. |
| select:flexdatalist | Selected item object | Fired when the user picks an option from the dropdown. |
| before:flexdatalist.data | undefined |
Fired before loading data (static or remote). |
| after:flexdatalist.data | Data array | Fired after data is loaded. |
| before:flexdatalist.search | {keywords, data} |
Fired before local search filtering begins. |
| after:flexdatalist.search | {keywords, data, results} |
Fired after local search filtering, before rendering results. |
| show:flexdatalist.results | Results array | Fired before results dropdown is shown. |
| shown:flexdatalist.results | Results array | Fired after results dropdown is rendered. |
| item:flexdatalist.results | Item object | Fired for each rendered result item. |
| empty:flexdatalist.results | null |
Fired when a search yields no results. |
| before:flexdatalist.toggle | {value, text, action} |
Fired before a multiple value is toggled. action is "add" or "remove". |
| after:flexdatalist.toggle | {value, text, action} |
Fired after a multiple value is toggled. |
| before:flexdatalist.remove | Value string being removed | Fired before a tag/value is removed. |
| after:flexdatalist.remove | {value, text} |
Fired after a tag/value has been removed. Reflects the new stored state. |
| before:flexdatalist.remove.all | Current value array | Fired before all values are cleared (multiple mode). |
| after:flexdatalist.remove.all | [] |
Fired after all values have been cleared. |
| addnew:flexdatalist | Keyword string | Fired when the user clicks "Add new item". e.detail is the typed keyword. |
| clear:flexdatalist | null |
Fired when the value is fully cleared. |
| init:flexdatalist | Options object | Fired after initial data loading completes (resolves instance.ready). |
Event Listener Example
const el = document.querySelector('#city'); // Using the fd instance (chainable) const [fd] = Flexdatalist.init(el, { url: '/api/cities' }); fd.on('select:flexdatalist', e => { console.log('Selected:', e.detail); // e.detail → { id: 42, name: 'Paris', country: 'FR' } }); // Or using native DOM event (dispatched on the <input> element) el.addEventListener('change:flexdatalist', e => { console.log('Value changed:', e.detail.value, '→', e.detail.text); });
Keyboard Shortcuts
Available while the alias input is focused.
Framework Adapters
Use Flexdatalist as a native component in Vue, React, or Svelte. Each adapter is a thin wrapper (~5 kB) — the core does all the heavy lifting.
| Framework | Package | Install |
|---|---|---|
| Vue 3 | flexdatalist-vue | npm install flexdatalist flexdatalist-vue |
| React 18/19 | flexdatalist-react | npm install flexdatalist flexdatalist-react |
| Svelte 4/5 | flexdatalist-svelte | npm install flexdatalist flexdatalist-svelte |
Vue 3
<script setup> import { ref } from 'vue'; import { Flexdatalist } from 'flexdatalist-vue'; import 'flexdatalist/css'; const city = ref(''); </script> <template> <Flexdatalist v-model="city" url="/api/cities" :min-length="2" @select="(item) => console.log(item)" /> </template>
React
import { Flexdatalist } from 'flexdatalist-react'; import 'flexdatalist/css'; function App() { return ( <Flexdatalist url="/api/cities" minLength={2} onSelect={(item) => console.log(item) } /> ); }
Svelte
<script> import { Flexdatalist } from 'flexdatalist-svelte'; import 'flexdatalist/css'; let city = ''; </script> <Flexdatalist bind:value={city} url="/api/cities" minLength={2} on:select={(e) => console.log(e.detail) } />
All options are available as component props in every adapter.
All events are re-emitted as framework-native events (@select, onSelect, on:select).
Instance methods are exposed via refs — getValue(), setValue(), search(), clear(), etc.
Standalone use still works — just use <script src="flexdatalist.js"> with no framework.
Migrating from jQuery Plugin
Key differences when upgrading from the original jquery.flexdatalist.js.
Before (jQuery)
$('#el').flexdatalist({ url: '/api' }); $('#el').flexdatalist('value'); $('#el').flexdatalist('value', '42'); $('#el').flexdatalist('add', 'val'); $('#el').flexdatalist('remove', 'val'); $('#el').flexdatalist('disabled', true); $('#el').flexdatalist('destroy'); $('#el').on('change:flexdatalist', function(evt, set, opts) { console.log(set.value, set.text); });
After (ES6 Class)
const [fd] = await Flexdatalist.init('#el', { url: '/api' }); fd.getValue(); fd.getText(); // ['Paris', 'London'] fd.getText('string'); // 'Paris,London' fd.getText(' | '); // 'Paris | London' fd.setValue('42'); fd.addValue('val'); fd.removeValue('val'); fd.disable(); fd.destroy(); fd.on('change:flexdatalist', e => { console.log(e.detail.value, e.detail.text); }); // or: el.addEventListener('change:flexdatalist', e => ...)
CSS: No changes — same class names. Keep using jquery.flexdatalist.css.
HTML data-* attributes: Unchanged — all options work the same way.
Events: Same event names. Payload moved from jQuery extra args to e.detail (CustomEvent).
Alias input: No longer given a name attribute — never double-submitted.