Customizable Select Box & Input Field Enhancement Library – Choices.js

Category: Form , Javascript , Recommended | March 14, 2025
Author:Choices-js
Views Total:136 views
Official Page:Go to website
Last Update:March 14, 2025
License:MIT

Preview:

Customizable Select Box & Input Field Enhancement Library – Choices.js

Description:

Choices.js is a vanilla JavaScript plugin that converts the normal select or input into customizable select inputs with multi-select and autocomplete support.

Great for creating multi-select tagging systems.

How to use it:

1. Install & download.

# Yarn
$ yarn add choices.js
# NPM
$ npm i choices.js

2. Load the following JavaScript and Stylesheet in your document.

<link rel="stylesheet" href="/public/assets/styles/choices.min.css" />
<script src="/public/assets/scripts/choices.min.js"></script>

3. Create a simple tag input from a normal text field that

<input id="demo-1" type="text" value="tag-1,tag-2" placeholder="Enter something">
var firstElement = document.getElementById('demo-1');
var choices1 = new Choices(firstElement, {
    delimiter: ',',
    editItems: true,
    maxItems: 5,
    removeButton: true
});

4. Create a multiple select input.

<select name="demo-2" id="demo-2" placeholder="This is a placeholder" multiple>
  <option value="Dropdown item 1">Dropdown item 1</option>
  <option value="Dropdown item 2">Dropdown item 2</option>
  <option value="Dropdown item 3" selected>Dropdown item 3</option>
  <option value="Dropdown item 4" disabled>Dropdown item 4</option>
</select>
var secondElement = new Choices('#demo-2', { allowSearch: false }).setValue(['Set value 1', 'Set value 2']);

5. Create a multiple select input that loads remote data via AJAX.

<select name="demo-3" id="demo-3" data-choice placeholder="Pick an Arctic Monkeys record"></select>
var choicesAjax = new Choices('#demo-2').ajax((callback) => {
    fetch('https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW')
        .then((response) => {
            response.json().then(function(data) {  
                callback(data.releases, 'title', 'title');
            });
        })
        .catch((error) => {
            callback();
        });
})

6. All possible options and callbacks with default values.

// pre-selected items
// an array of strings
// or an array of objects
items: [],
/* add choices to select input
[{
  value: 'Option 1',
  label: 'Option 1',
  selected: true,
  disabled: false,
},
{
  value: 'Option 2',
  label: 'Option 2',
  selected: false,
  disabled: true,
  customProperties: {
    description: 'Custom description about Option 2',
    random: 'Another random custom property'
  },
}]
*/
choices: [],
// suppress console errors and warnings
silent: false,
// the amount of choices to be rendered within the dropdown list
renderChoiceLimit: -1,
// the amount of items a user can input/select
maxItemCount: -1,
// Control how the dropdown closes after making a selection for select-one or select-multiple.
// Boolean | 'auto'
closeDropdownOnSelect: 'auto',
// Make select-multiple with a max item count of 1 work similar to select-one does. 
// Selecting an item will auto-close the dropdown and swap any existing item for the just selected choice. 
// If applied to a select-one, it functions as above and not the standard select-one.
singleModeForMultiSelect: false,
// Whether a user can add choices dynamically.
addChoices: false,
// allows users to add new items
addItems: true,
// a RegExp or string (will be passed to RegExp constructor internally) or filter function that will need to return true for a user to successfully add an item
addItemFilter: null,
// allows users to remove items
removeItems: true,
// shows remove button
removeItemButton: false,
// Align item remove button left vs right
removeItemButtonAlignLeft: false,
// allows users to edit items
editItems: false,
// allows to render HTML
allowHTML: true,
// Whether HTML should be escaped on input when addItems or addChoices is true. 
// If false, user input will be treated as plain text. 
// If true, this can be used to perform XSS scripting attacks if you load choices from a remote source.
allowHtmlUserInput: false,
// allows to duplicate inputted/chosen items
duplicateItemsAllowed: true,
// custom delimiter
delimiter: ',',
// allows copy & paste
paste: true,
// enable live search
searchEnabled: true,
// whether choices should be filtered by input or not
searchChoices: true,
// the minimum length a search value should be before choices are searched
searchFloor: 1,
// the maximum amount of search results to show
searchResultLimit: 4,
// search fields
searchFields: 'label', 'value',
// or 'top', 'bottom'
position: 'auto',
// reset scroll position after new items have been added
resetScrollPosition: true,
// whether to sort choices and groups
shouldSort: true,
// whether to sort items
shouldSortItems: false,
/*
sorter: function(a, b) {
  return b.label.length - a.label.length;
}, 
*/
sorter: utils_1.sortByAlpha,
// You can pass along the shadowRoot from your application
shadowRoot: null,
// whether to show a placeholder
placeholder: true,
// placeholder value
placeholderValue: null,
// placeholder value for search field
searchPlaceholderValue: null,
// prepend a value to each item added/selected
prependValue: null,
// append a value to each item added/selected
appendValue: null,
// whether selected choices should be removed from the list
// or 'always'
renderSelectedChoices: 'auto',
appendGroupInSearch: false,
// custom messages
loadingText: 'Loading...',
noResultsText: 'No results found',
noChoicesText: 'No choices to choose from',
itemSelectText: 'Press to select',
uniqueItemText: 'Only unique values can be added',
customAddItemText: 'Only values matching specific conditions can be added',
// functions
addItemText: function (value) {
  return "Press Enter to add <b>\"".concat((0, utils_1.sanitise)(value), "\"</b>");
},
maxItemText: function (maxItemCount) {
  return "Only ".concat(maxItemCount, " values can be added");
},
removeItemIconText: () => `Remove item`,
removeItemLabelText: (value) => `Remove item: ${value}`,
valueComparer: function (value1, value2) {
  return value1 === value2;
},
// fuse library options
// https://fusejs.io/api/options.html
fuseOptions: {
  includeScore: true
},
// label ID to improve a11y
labelId: '',
// callbacks
callbackOnInit: function(e){
  // ...
},
callbackOnCreateTemplates: : function(template){
  // ...
},
// default CSS class names
classNames: {
  containerOuter: 'choices',
  containerInner: 'choices__inner',
  input: 'choices__input',
  inputCloned: 'choices__input--cloned',
  list: 'choices__list',
  listItems: 'choices__list--multiple',
  listSingle: 'choices__list--single',
  listDropdown: 'choices__list--dropdown',
  item: 'choices__item',
  itemSelectable: 'choices__item--selectable',
  itemDisabled: 'choices__item--disabled',
  itemChoice: 'choices__item--choice',
  description: 'choices__description',
  placeholder: 'choices__placeholder',
  group: 'choices__group',
  groupHeading: 'choices__heading',
  button: 'choices__button',
  activeState: 'is-active',
  focusState: 'is-focused',
  openState: 'is-open',
  disabledState: 'is-disabled',
  highlightedState: 'is-highlighted',
  selectedState: 'is-selected',
  flippedState: 'is-flipped',
  loadingState: 'is-loading',
  notice: 'choices__notice',
  addChoice: 'choices__item--selectable', 'add-choice',
  noResults: 'has-no-results',
  noChoices: 'has-no-choices',
}

7. API methods.

const intance = new Choices(element, {
  // options here
});
// init
intance.init();
// destroy
intance.destroy();
// refresh
intance.refresh(withEvents: boolean = false, selectFirstOption: boolean = false);
// enable/disable
intance.enable();
intance.disable();
// highlight/unhighlight each chosen item
intance.highlightAll();
intance.unhighlightAll();
// remove items
intance.removeActiveItemsByValue(value);
intance.removeActiveItems(excludedId);
intance.removeHighlightedItems(runEvent?: boolean);
// show/hide the dropdown
intance.showDropdown();
intance.hideDropdown();
// set choices of select input via an array of objects (or function that returns array of object or promise of it), a value field name and a label field name.
intance.setChoices(choicesArrayOrFetcher?: (InputChoice | InputGroup)[] | ((instance: Choices) => (InputChoice | InputGroup)[] | Promise<(InputChoice | InputGroup)[]>), value?: string | null, label?: string, replaceChoices?: boolean): this | Promise;
// clear all choices
intance.clearChoices();
// get value(s)
intance.getValue(valueOnly)
// set value(s)
intance.setValue(items: string[] | InputChoice[]);
// set choice(s)
intance.setChoiceByValue(value: string | string[]);
// remove all items, choices and groups
intance.clearStore();
// clear input
intance.clearInput();

8. Events.

element.addEventListener(
  'change',
  function(event) {
    // each time an item is added & removed
    console.log(event.detail.value);
  },
  false,
);
element.addEventListener(
  'addItem',
  function(event) {
    // each time an item is added
    console.log(event.detail.id);
    console.log(event.detail.value);
    console.log(event.detail.label);
    console.log(event.detail.customProperties);
    console.log(event.detail.groupValue);
    console.log(event.detail.keyCode);
  },
  false,
);
element.addEventListener(
  'removeItem',
  function(event) {
    // each time an item is removed
    console.log(event.detail.id);
    console.log(event.detail.value);
    console.log(event.detail.label);
    console.log(event.detail.customProperties);
    console.log(event.detail.groupValue);
  },
  false,
);
element.addEventListener(
  'highlightItem',
  function(event) {
    // each time an item is highlighted
    console.log(event.detail.id);
    console.log(event.detail.value);
    console.log(event.detail.label);
    console.log(event.detail.groupValue);
  },
  false,
);
element.addEventListener(
  'unhighlightItem',
  function(event) {
    // each time an item is unhighlighted
    console.log(event.detail.id);
    console.log(event.detail.value);
    console.log(event.detail.label);
    console.log(event.detail.groupValue);
  },
  false,
);
element.addEventListener(
  'choice',
  function(event) {
    // each time a choice is selected
    console.log(event.detail.choice);
  },
  false,
);
element.addEventListener(
  'highlightChoice',
  function(event) {
    // fired when a choice from the dropdown is highlighted
    console.log(event.detail.el);
  },
  false,
);
element.addEventListener(
  'search',
  function(event) {
    // fired when a user search choices
    console.log(event.detail.value);
    console.log(event.detail.resultCount);
  },
  false,
);
element.addEventListener(
  'showDropdown',
  function(event) {
    // fired when the dropdown is shown
  },
  false,
);
element.addEventListener(
  'hideDropdown',
  function(event) {
    // fired when the dropdown is hidden
  },
  false,
);

Changelog:

v11.1.0 (03/14/2025)

  • Support <option> label attribute
  • add KMP search algorithm (gated by build flag)
  • Remove role=”textbox” from search input, per a11y practices.

v11.0.6 (02/27/2025)

  • Bugfixes
  • Changes to setChoices & clearChoices adjust how the selection and new choices combine when using replaceChoices: true is used to better match v10.2.0 and v11.0.3 behavior.

v11.0.5 (02/26/2025)

  • Bugfixes

v11.0.4 (02/23/2025)

  • Lots of bugs fixed
  • Do not preventDefault on item to support dragging

v11.0.3 (12/28/2024)

  • Lots of bugs fixed

v11.0.2 (09/08/2024)

  • duplicateItemsAllowed option is now respected by setChoices() method

v11.0.2 (09/08/2024)

  • duplicateItemsAllowed option is now respected by setChoices() method
  • Pass getClassNames as the 3rd argument to callbackOnCreateTemplates callback
  • Bug Fixes

v11.0.1 (08/29/2024)

  • Fix form reset/clearStore method

v11.0.0 (08/27/2024)

  • Improve performance of search/filtering with large number of choices.
  • Improve performance of rendering a large number of items or choices and adding/removing items.
  • Add closeDropdownOnSelect option, controls how the dropdown is close after selection is made.
  • Allow choices.js to be imported on nodejs, useful for tests and also server side rendering. As windows.document is by default not defined, the default template rendering will not function. The callbackOnCreateTemplates callback must be used.
  • config.classNames now accept arrays to support multiple classes.
  • The original option list for the select is not destroyed, and all loaded choices are serialised to HTML for better compatibility with external javascript.
  • New singleModeForMultiSelect feature to treat a select-single as if it was a select-multiple with a max item count of 1, and still auto-close the dropdown and swap the active item on selection.
  • Remove item text can be localized.
  • Allow user-created choices for selects. User input is escaped by default. At the risk of XSS attacks this can be disabled by allowHtmlUserInput.
  • Render options without a group even if groups are present.
  • Read data-labelclass/data-label-description from <option> HTML to drive adding a per-choice CSS label and description text when allowHtml: false.
  • Add removeItemButtonAlignLeft option, to control if the remove item button is at the start or the end of the item.
  • Add removeChoice method. Removes the choice from the choices.js object and any backing <option> HTML element
  • Add refresh method. Reloads choices from the backing <select>s options.
  • escapeForTemplate function is passed to the 2nd method of the callbackOnCreateTemplates callback.
  • When allowHtml is false, default templates now render escaped html to innerHtml writing to innerText. This provides consistent rendering performance as innerText is quirky and slower than escaped html into innerHtml
  • Shadow DOM support
  • searchResultLimit can be set to -1 for no limit of search results to display.
  • Bug Fixes

v10.2.0 (11/30/2022)

  • Add JSON support to custom properties
  • Allow overwrite of the $choices-z-index variable
  • Bug Fixes

v10.1.0 (02/17/2022)

  • Add option labelId to improve a11y
  • Bug Fixes

v10.0.0 (01/03/2022)

  • Upgrade to Fuse v6
  • Introduce allowHTML option to allow people to disable injecting HTML into choices.
  • Bug Fixes

v9.1.0 (12/20/2021)

  • Bug Fixes
  • Switch to dart-sass, fix npm audit issues
  • Documentation of input type terms
  • Solve deprecated warning using / for division outside of calc()
  • Update release drafter to latest
  • Convert to typescript
  • Adds a variable for the z-index

v9.0.1 (11/18/2019)

  • Bugfix

v9.0.0 (11/15/2019)

  • Add missing type definitions + rename sortFn.
  • Bugs fixed.

v8.0.0 (11/03/2019)

  • The ability to pass multiple elements to one instance of Choices has been removed – now only one element can be associated with Choices
  • The undocumented userDefaults static property has been removed
  • The ajax method has been removed. setChoices can now be used to set choices dynamically
  • The addItemFilterFn option has been renamed to addItemFilter and now supports regex’s
  • Element.prototype.closest has been added to the required polyfill list
  • The .is-hidden class has been replaced with the hidden attribute
  • Bugs fixed
  • Code refactoring

v7.1.5 (10/24/2019)

  • Bugs fixed

v7.1.0 (10/22/2019)

  • Bugs fixed

v7.0.3 (10/22/2019)

  • Bugs fixed

02/23/2019

  • v6.0.2: Resolve undefined error

02/19/2019

  • v6.0.1

02/13/2019

  • v6.0.0: callback to filter items before adding

02/12/2019

  • v5.1.0: callback to filter items before adding

01/25/2019

  • v4.1.4: Bugfix

11/26/2018

  • v4.1.3: Fix set choice by value bug

11/25/2018

  • v4.1.2: Fix form submission bug in firefox

11/03/2018

  • v4.1.0: Disable input when loading remote data

10/31/2018

  • v4.0.6: Disable at a later stage of intialising

You Might Be Interested In:


10 thoughts on “Customizable Select Box & Input Field Enhancement Library – Choices.js

  1. Victor

    i created a REST call api for my Dbase, what parameters could be used for ur API?
    http://localhost/ivr/new/Choices-master/rest_api.php/system_users

    var choicesAjax = new Choices(‘#demo-2’).ajax((callback) => {
    fetch(‘https://api.discogs.com/artists/391170/releases?token=QBRmstCkwXEvCjTclCpumbtNwvVkEzGAdELXyRyW’)
    .then((response) => {
    response.json().then(function(data) {
    callback(data.releases, ‘title’, ‘title’);
    });
    })
    .catch((error) => {
    callback();
    });
    })

    Reply
  2. Youne

    I could create a simple tag input from a normal text field but the end user can not edit the tags created. Is that possible please ? Thanks.

    Reply
  3. adar

    why i get this error : TypeError: (new Choices(…)).ajax is not a function ??

    Reply
  4. JuanPa

    add one item
    .setValue([‘Set value 1’]);

    how remove these item with a function?
    No a button or the spacebar

    Reply
    1. Chritian

      Hello, i try

      element = new coices…
      element.setValue….
      element.clearChoices()

      But it not works…

      Reply
  5. Nelson

    watch out for the example!!!!! this doesn’t work anymore:
    maxItems: 5,
    removeButton: true

    I changed that for this:
    maxItemCount: -1,
    removeItemButton: false,

    Reply
  6. Ore

    hi, i am unable to put > symbol as it encodes to &rt; can you please suggest where to update this ? to show just > symbols

    Reply

Leave a Reply