Choices.js is a lightweight (~20kb gzipped), vanilla JavaScript select box and text input plugin that serves as an alternative to jQuery-dependent libraries like Select2 and Selectize. It provides comprehensive customization options for creating enhanced select elements, multi-select interfaces, and text inputs with features including configurable sorting, flexible CSS styling, fast search and filtering capabilities, right-to-left language support, and custom templating.
npm install choices.jsimport Choices from "choices.js";For CommonJS:
const Choices = require("choices.js");import Choices from "choices.js";
// Basic select enhancement
const element = document.querySelector('.js-choice');
const choices = new Choices(element);
// Text input with configuration
const textInput = new Choices('#text-input', {
delimiter: ',',
editItems: true,
maxItemCount: 5,
removeItemButton: true,
});
// Pre-configure choices for select
const selectElement = new Choices('#select-element', {
choices: [
{ value: 'one', label: 'Option One' },
{ value: 'two', label: 'Option Two', selected: true },
{ value: 'three', label: 'Option Three', disabled: true },
]
});Choices.js is built around several key components:
Choices class providing the primary API for initialization and controlMain plugin class providing initialization, configuration, and control methods for enhanced select and input elements.
class Choices {
constructor(
element?: string | Element | HTMLInputElement | HTMLSelectElement,
userConfig?: Partial<Options>
);
// Static properties
static version: string;
static defaults: {
options: Partial<Options>;
allOptions: Options;
templates: Templates;
};
// Instance properties
readonly initialised: boolean;
readonly initialisedOK?: boolean;
readonly config: Options;
// Lifecycle methods
init(): void;
destroy(): void;
enable(): this;
disable(): this;
// Value management
getValue<B extends boolean = false>(valueOnly?: B): EventChoiceValueType<B> | EventChoiceValueType<B>[];
setValue(items: string[] | InputChoice[]): this;
setChoiceByValue(value: string | string[]): this;
setChoices(
choicesArrayOrFetcher:
| (InputChoice | InputGroup)[]
| ((instance: Choices) => (InputChoice | InputGroup)[] | Promise<(InputChoice | InputGroup)[]>),
value?: string | null,
label?: string,
replaceChoices?: boolean,
clearSearchFlag?: boolean
): this | Promise<this>;
refresh(withEvents?: boolean, selectFirstOption?: boolean, deselectAll?: boolean): this;
// Item management
highlightItem(item: InputChoice, runEvent?: boolean): this;
unhighlightItem(item: InputChoice, runEvent?: boolean): this;
highlightAll(): this;
unhighlightAll(): this;
removeActiveItemsByValue(value: string): this;
removeActiveItems(excludedId?: number): this;
removeHighlightedItems(runEvent?: boolean): this;
// Choice management
removeChoice(value: string): this;
clearChoices(): this;
clearStore(clearOptions?: boolean): this;
clearInput(): this;
// UI control
showDropdown(preventInputFocus?: boolean): this;
hideDropdown(preventInputBlur?: boolean): this;
}Comprehensive configuration system covering behavior, styling, validation, and functionality customization.
interface Options {
// Core behavior
silent: boolean;
items: string[] | InputChoice[];
choices: InputChoice[] | InputGroup[];
maxItemCount: number;
closeDropdownOnSelect: boolean | 'auto';
// Search and filtering
searchEnabled: boolean;
searchChoices: boolean;
searchFloor: number;
searchResultLimit: number;
// Styling and templates
classNames: ClassNames;
position: PositionOptionsType;
}Event system providing lifecycle hooks and user interaction callbacks for integration with external systems.
const EventType = {
showDropdown: 'showDropdown',
hideDropdown: 'hideDropdown',
change: 'change',
choice: 'choice',
search: 'search',
addItem: 'addItem',
removeItem: 'removeItem',
highlightItem: 'highlightItem',
highlightChoice: 'highlightChoice',
unhighlightItem: 'unhighlightItem',
} as const;Customizable HTML template functions for complete control over rendered UI components and styling.
interface Templates {
containerOuter: (classNames, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType, labelId) => HTMLElement;
containerInner: (classNames) => HTMLElement;
itemList: (classNames, isSelectOneElement) => HTMLElement;
placeholder: (classNames, value) => HTMLElement;
item: (classNames, data, removeItemButton, removeItemButtonAlignLeft, removeItemText) => HTMLElement;
choiceList: (classNames, isSelectOneElement) => HTMLElement;
choiceGroup: (classNames, data) => HTMLElement;
choice: (classNames, data, itemSelectText, groupName) => HTMLElement;
input: (classNames, placeholderValue, searchEnabled) => HTMLElement;
dropdown: (classNames) => HTMLElement;
notice: (classNames, label, type) => HTMLElement;
}// Core data structures
interface InputChoice {
id?: number;
highlighted?: boolean;
labelClass?: string | Array<string>;
labelDescription?: string;
customProperties?: CustomProperties;
disabled?: boolean;
active?: boolean;
label: StringUntrusted | string;
placeholder?: boolean;
selected?: boolean;
value: any;
}
interface InputGroup {
id?: number;
active?: boolean;
disabled?: boolean;
label?: StringUntrusted | string;
value: string;
choices: InputChoice[];
}
// Event choice types
interface EventChoice extends InputChoice {
element?: HTMLOptionElement | HTMLOptGroupElement;
groupValue?: string;
keyCode?: number;
}
type EventChoiceValueType<B extends boolean> = B extends true ? string : EventChoice;
// Event types
type EventTypes = 'showDropdown' | 'hideDropdown' | 'change' | 'choice' | 'search' | 'addItem' | 'removeItem' | 'highlightItem' | 'highlightChoice' | 'unhighlightItem';
// Configuration types
type PositionOptionsType = 'auto' | 'top' | 'bottom';
type PassedElementType = 'text' | 'select-one' | 'select-multiple';
// String safety types
interface StringUntrusted {
_type: 'StringUntrusted';
value: string;
}
interface StringPreEscaped {
_type: 'StringPreEscaped';
value: string;
}
// Additional utility types
type CustomProperties = Record<string, any>;
type NoticeType = 'noChoices' | 'noResults' | 'addChoice';
type FilterFunction = (value: string) => boolean;
type NoticeStringFunction = (value: string, valueRaw?: string) => string;
type NoticeLimitFunction = (maxItemCount: number) => string;
type StringFunction = () => string;
type ValueCompareFunction = (choice1: string, choice2: string) => boolean;