Autocomplete - Simple accessible autocomplete for vanilla javacript with support for remote & local data, ~3KB gzip

Overview

autocomplete

Simple autocomplete with asynchronous data fetch

Demo

See the demo - example

Features

  • Accessible, with full support for ARIA attributes and keyboard interactions
  • Customize your own CSS
  • Support for asynchronous data fetching
  • Move between the records using the arrows , and confirm by Enter or mouse
  • Grouping of record results
  • Showing 'no results'
  • Show all values on click
  • No dependencies
  • Very light library, packed gzip only ~3KB
  • And a lot more

Installation

CDN

CSS

">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/tomik23/[email protected]/dist/css/autocomplete.min.css"/>

JavaScript

">
<script src="https://cdn.jsdelivr.net/gh/tomik23/[email protected]/dist/js/autocomplete.min.js">script>
-- OR --

Download from docs folder and insert to html:

  • autocomplete.css
  • autocomplete.min.js

HTML

Basic code to display autocomplete correctly

">
<div class="auto-search-wrapper">
  <input type="text" id="local" autocomplete="off" placeholder="Enter letter" />
div>

JavaScript

window.addEventListener('DOMContentLoaded', function () {
  // 'local' is the 'id' of input element
  new Autocomplete('local', {
    onSearch: ({ currentValue }) => {
      // local data
      const data = [
        { name: 'Walter White' },
        { name: 'Jesse Pinkman' },
        { name: 'Skyler White' },
        { name: 'Walter White Jr.' },
      ];
      return data
        .sort((a, b) => a.name.localeCompare(b.name))
        .filter((element) => {
          return element.name.match(new RegExp(currentValue, 'i'));
        });
    },

    onResults: ({ matches }) => {
      return matches
        .map((el) => {
          return `
            
  • ${el.name}
  • `
    ; }) .join(''); }, }); });

    Package Manager

    Before the first use, clone this repository and install node dependencies:

    git clone https://github.com/tomik23/autocomplete.git
    
    yarn
    // or
    npm install

    Run the app

    Run the development version:

    yarn dev
    // or
    npm run dev

    Run the production version:

    yarn prod
    // or
    npm run prod

    Configuration of the plugin

    props type default require description
    element string Input field id
    onSearch function Function for user input. It can be a synchronous function or a promise
    onResults function Function that creates the appearance of the result
    onSubmit function Executed on input submission
    onOpened function returns two variables 'results' and 'showItems', 'resutls' first rendering of the results 'showItems' only showing the results when clicking on the input field
    onSelectedItem function Get index and data from li element after hovering over li with the mouse or using arrow keys ↓/↑
    onReset function After clicking the 'x' button
    onRender function Possibility to add html elements, e.g. before and after the search results
    onClose function e.g. delete class after close results, see example modal
    noResults function Showing information: "no results"
    destroy method Removes the autocomplete instance and its bindings
    clearButton boolean true A parameter set to 'true' adds a button to remove text from the input field
    selectFirst boolean false Default selects the first item in the list of results
    insertToInput boolean false Adding an element selected with arrows to the input field
    disableCloseOnSelect boolean false Prevents results from hiding after clicking on an item from the results list
    showAllValues boolean false This option will toggle showing all values when the input is clicked, like a default dropdown
    cache boolean false The characters entered in the input field are cached
    howManyCharacters number 1 The number of characters entered should start searching
    delay number 500 Time in milliseconds that the component should wait after last keystroke before calling search function 1000 = 1s
    ariaLabelClear string clear text from input Set aria-label attribute for the clear button
    classPreventClosing string Prevents results from hiding after clicking on element with this class
    classGroup string Enter a class name, this class will be added to the group name elements
    classPrefix string Prefixing all autocomplete css class name, 'prefix-auto-', default 'auto-'
    instruction string When autocomplete results ... aria-describedby attribute A full text below

    instructions - has been removed from the library, see how to add to html

    How do I add data to the input field?

    Simple data

    onResults: ({ matches }) => {
      return matches
        .map((el) => {
          return `
            
  • ${el.name}
  • `
    ; }) .join(''); };

    A complicated example

    The example below displays ${el.name}, first name and last name as well as ${el.img} photo in the results. From this example, only the first element will be added to the input field. So ${el.name} no matter if it will be inside p, div, span etc. Always the first element and it's only text so it can even be in this form

    ${el.name}

    `; }) .join(''); };">
    onResults: ({ matches }) => {
      return matches
        .map((el) => {
          return `
            
  • ${el.name}

    ${el.img}">

  • `
    ; }) .join(''); };

    Usage jquery || axios || promise + fetch

    JAVASCRIPT

    ${el.status} ${count(el.status)}` : ''; // this part is responsible for the appearance // in the drop-down list - see the example in index.html // remember only the first element from
  • is put // into the input field, in this case the text // from the

    element return ` ${group}

  • ${el.name.replace(regex, (str) => `${str}`)}

    ${el.name}

    nickname: - ${el.nickname}
    birthday: - ${el.birthday}
    status: - ${el.status}
  • `; }) .join(''); }, // the onSubmit function is executed when the user // submits their result by either selecting a result // from the list, or pressing enter or mouse button onSubmit: ({ index, element, object, results }) => { console.log('complex: ', index, element, object, results); // window.open(`https://www.imdb.com/find?q=${encodeURI(input)}`) }, // get index and data from li element after // hovering over li with the mouse or using // arrow keys ↓ | ↑ onSelectedItem: ({ index, element, object }) => { console.log('onSelectedItem:', index, element.value, object); }, // the calback function presents no results noResults: ({ element, currentValue, template }) => template(`
  • No results found: "${currentValue}"
  • `), });">
    new Autocomplete('complex', {
      // search delay
      delay: 1000,
    
      // add button 'x' to clear the text from
      // the input filed
      // by default is true
      clearButton: false,
    
      // default selects the first item in
      // the list of results
      // by default is false
      selectFirst: true,
    
      // add text to the input field as you move through
      // the results with the up/down cursors
      // by default is false
      insertToInput: true,
    
      // the number of characters entered
      // should start searching
      // by default is 1
      howManyCharacters: 2,
    
      // the characters entered in
      // the input field are cached
      // by default is false
      cache: true,
    
      // prevents results from hiding after
      // clicking on an item from the list
      // by default is false
      disableCloseOnSelect: true,
    
      // enter the name of the class by
      // which you will name the group element
      // by default is empty ''
      classGroup: 'group-by',
    
      // prevents results from hiding after
      // clicking on element with this class
      // footer/header elements have this class
      // of course, any class name
      // by default is empty ''
      classPreventClosing: 'additional-elements',
    
      // prefixing all autocomplete css class name,
      // 'prefix-auto-', default 'auto-'
      classPrefix: 'prefix',
    
      // this option will toggle showing all
      // values when the input is clicked,
      // like a default dropdown
      // by default is false
      showAllValues: true,
    
      // set aria-label attribute for the clear button
      // by default is 'clear text from input'
      ariaLabelClear: 'insert your text if you want ;)'
    
      // Function for user input. It can be a synchronous function or a promise
      // you can fetch data with jquery, axios, fetch, etc.
      onSearch: ({ currentValue }) => {
        // static file
        // const api = './characters.json';
    
        // OR -------------------------------
    
        // your REST API
        const api = `https://breakingbadapi.com/api/characters?name=${encodeURI(currentValue)}`;
        /**
         * jquery
         * If you want to use jquery you have to add the
         * jquery library to head html
         * https://cdnjs.com/libraries/jquery
         */
        return $.ajax({
          url: api,
          method: 'GET',
        })
          .done(function (data) {
            return data;
          })
          .fail(function (xhr) {
            console.error(xhr);
          });
    
        // OR ----------------------------------
    
        /**
         * axios
         * If you want to use axios you have to add the
         * axios library to head html
         * https://cdnjs.com/libraries/axios
         */
        return axios
          .get(api)
          .then((response) => {
            return response.data;
          })
          .catch((error) => {
            console.log(error);
          });
    
        // OR ----------------------------------
    
        /**
         * Promise
         */
        return new Promise((resolve) => {
          fetch(api)
            .then((response) => response.json())
            .then((data) => {
              resolve(data);
            })
            .catch((error) => {
              console.error(error);
            });
        });
      },
    
      // this part is responsible for the number of records,
      // the appearance of li elements and it really depends
      // on you how it will look
      onResults: ({ currentValue, matches, template, classGroup }) => {
        // const regex = new RegExp(^${input}`, 'gi'); // start with
        const regex = new RegExp(currentValue, 'gi');
    
        // counting status elements
        function count(status) {
          let count = {};
          matches.map((el) => {
            count[el.status] = (count[el.status] || 0) + 1;
          });
          return `${count[status]} items`;
        }
    
        // checking if we have results if we don't
        // take data from the noResults collback
        return matches === 0
          ? template
          : matches
              .sort(
                (a, b) =>
                  a.status.localeCompare(b.status) || a.name.localeCompare(b.name)
              )
              .map((el, index, array) => {
                // we create an element of the group
                let group =
                  el.status !== array[index - 1]?.status
                    ? `
  • ${classGroup}">${el.status} ${count(el.status)}
  • `
    : ''; // this part is responsible for the appearance // in the drop-down list - see the example in index.html // remember only the first element from
  • is put // into the input field, in this case the text // from the

    element return ` ${group}

  • ${el.name.replace(regex, (str) => `${str}`)}

    ${el.img}" style="max-width: 67px;max-height:95px">

    ${el.name}

    nickname: - ${el.nickname}
    birthday: - ${el.birthday}
    status: - ${el.status}
  • `
    ; }) .join(''); }, // the onSubmit function is executed when the user // submits their result by either selecting a result // from the list, or pressing enter or mouse button onSubmit: ({ index, element, object, results }) => { console.log('complex: ', index, element, object, results); // window.open(`https://www.imdb.com/find?q=${encodeURI(input)}`) }, // get index and data from li element after // hovering over li with the mouse or using // arrow keys ↓ | ↑ onSelectedItem: ({ index, element, object }) => { console.log('onSelectedItem:', index, element.value, object); }, // the calback function presents no results noResults: ({ element, currentValue, template }) => template(`
  • No results found: "${currentValue}"
  • `
    ), });

    All available configuration items

    const auto = new Autocomplete('you-id', {
      clearButton: false,
      selectFirst: false,
      insertToInput: false,
      disableCloseOnSelect: false,
      cache: false,
      showAllValues: true,
      classPreventClosing: '',
      classGroup: '',
      classPrefix: 'auto',
      howManyCharacters: 1,
      ariaLabelClear: 'clear text from input',
      delay: 500,
      onSearch: ({ currentValue, element }) => {},
      onResults: ({ currentValue, matches, template, classGroup }) => {},
      onRender: ({ element, results }) => {},
      onSubmit: ({ index, element, object, results }) => {},
      onOpened: ({ type, element, results }) => {},
      onSelectedItem: ({ index, element, object }) => {},
      onReset: (element) => {},
      onClose: () => {},
      noResults: ({ element, currentValue, template }) => {},
    });
    
    // public methods
    auto.destroy();

    Browsers support

    Edge
    Edge
    Firefox
    Firefox
    Chrome
    Chrome
    Opera
    Opera
    Vivaldi
    Vivaldi
    Edge last 2 versions last 2 versions last 2 versions last 2 versions

    * If you want the code to be supported in IE11 you need replace a few lines in package.json. Below what needs to be changed in the code and compile.

    Replace

    "production": [
      "defaults",
      "not IE 11",
      "maintained node versions"
    ]

    To this

    0.2%", "not dead", "not op_mini all" ]">
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ]

    IE10, IE11

    Will work if you use polyfill for promise and closest. There are three ways to add this polyfill:

    1. Add the following script to your html in head
    if (!('Promise' in window)) { var script = document.createElement('script'); script.src = 'https://polyfill.io/v3/polyfill.min.js?features=Promise%2CElement.prototype.closest'; document.getElementsByTagName('head')[0].appendChild(script); } ">
    <script type="text/javascript">
      if (!('Promise' in window)) {
        var script = document.createElement('script');
        script.src = 'https://polyfill.io/v3/polyfill.min.js?features=Promise%2CElement.prototype.closest';
        document.getElementsByTagName('head')[0].appendChild(script);
      }
    script>
    1. Add the script below to head in html
    ">
    <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js">script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/browser.min.js">script>
    1. Add pollyfill to Autocomplete.js and build the script again
    import 'promise-polyfill/src/polyfill';
    import './helpers/element-closest-polyfill.js';
    1. You can download all polyfills from docs/js/polyfill.js and put in head html
    ">
    <script src="./polyfill.js">script>

    License

    This project is available under the MIT license.

    Comments
    • Incorrect syntax at object properties

      Incorrect syntax at object properties

      It seems there's a syntax error at the properties here, and a tool like NUglify warns about this: https://github.com/trullock/NUglify/blob/master/src/NUglify/JavaScript/JSError.cs#L96

      It seems in should be the following instead using : instead of = operator:

      delay: 500,
      clearButton: true,
      howManyCharacters: 1,
      selectFirst: false,
      insertToInput: false,
      showAllValues: false,
      cache: false,
      disableCloseOnSelect: false,
      

      https://github.com/tomik23/autocomplete/blob/master/docs/js/autocomplete.js#L34-L54

      like you have here: https://github.com/tomik23/autocomplete/blob/master/docs/js/autocomplete.js#L7-L10

      question 
      opened by bjarnef 5
    • Connecting to livehelperchat

      Connecting to livehelperchat

      Is it possible to connect with livehelperchat (https://github.com/LiveHelperChat/livehelperchat ) chat widget for auto completion of questions ??

      2)Widget is loaded as iframe and target div is given below

      question 
      opened by sreenathmurali 3
    • Can't find the library on jsDelivr

      Can't find the library on jsDelivr

      Hi!

      I don't know where I can post this issue, so I do it here.

      When I do a search with the term autocomplete on the main page of jsDelivr I find a react component (react-circular-progress-bar) but not this library. I should say that I gave up after the 5. page, I've thought that if it's there it should be listed earlier.
      If I search tomik I just get the react library. If I click on the Author, only one library is reported.

      Maybe there're some configs missing, I have no idea. Anyway, I think it's not the supposed behaviour.

      Thanks for your work.

      ciao

      question 
      opened by LukMas 3
    • Theme

      Theme

      It would be nice to separate the styles, i.e. create a default style (minimal appearance) and a separate "theme" file that changes the appearance of the input field and ul, something like highlightjs-themes

      enhancement 
      opened by tomik23 2
    • Any way to update the count text while searching?

      Any way to update the count text while searching?

      Great autocomplete by the way and docs, covers almost all the bases including accessibility!

      On search I would like it to say 'Searching...' so two things would need to be in place for that: Currently searching and dropdown visible.

      Example:

      <li class="search-results-title" role="option" tabindex="-1" aria-selected="false" aria-setsize="11" aria-posinset="0">
           <span>Searching...</span>
      </li>
      
      <li class="search-results-title" role="option" tabindex="-1" aria-selected="false" aria-setsize="11" aria-posinset="0">
           <span>Found 10 results</span>
      </li>
      

      Thanks!

      opened by LukaszJaro 1
    • Deploy as NPM package

      Deploy as NPM package

      Hey @tomik23

      It would be super helpful to have this as an NPM package. It would be an improvement for modern local development but also to use it with tools like Skypack.

      opened by kilianso 1
    • Show all values

      Show all values

      The problem only occurs when we want to show all the results after clicking on the input field, and exactly when something is already selected or when there are no results.

      Click on the input field for the first time. The results appear all and everything is fine. Then select a field by clicking on it. The data is saved in the input field. Now I click on the input field again. And it is at this stage that this problem occurs. Because instead of displaying only one record, all data appears for a fraction of a second and then only this one record appears.

      bug 
      opened by tomik23 1
    • Results do not scroll to the top

      Results do not scroll to the top

      For example, enter data-elements enter 'a', use the arrows to move to the last record of results. Now, with open files, delete the letter 'a' and type 'b', the records should move to the top and mark the first record, but unfortunately it doesn't.

      bug 
      opened by tomik23 1
    • Reset margin on autocomplete ul

      Reset margin on autocomplete ul

      It seems this part need to reset margin as well: https://github.com/tomik23/autocomplete/blob/master/sources/scss/modules/_autocomplete.scss#L30-L34

      Depending on framework etc. there is typical a left margin and in that case it looks like this:

      image

      bug 
      opened by bjarnef 1
    • Up-down arrows item selection

      Up-down arrows item selection

      With a single list item return, it is possible for the up and down arrows to deselect the list item, which throws an error onSubmit (tested with static file, and leaflet/osm) note: leaflet test done using world's longest city name: Taumatawhakatangihangakoauauotamateaturipukakapikimaungahoronukupokaiwhenuakitanatahu

      bug 
      opened by aoedipus 1
    • chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2

      chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2

      Bumps decode-uri-component from 0.2.0 to 0.2.2.

      Release notes

      Sourced from decode-uri-component's releases.

      v0.2.2

      • Prevent overwriting previously decoded tokens 980e0bf

      https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.1...v0.2.2

      v0.2.1

      • Switch to GitHub workflows 76abc93
      • Fix issue where decode throws - fixes #6 746ca5d
      • Update license (#1) 486d7e2
      • Tidelift tasks a650457
      • Meta tweaks 66e1c28

      https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.1

      Commits

      Dependabot compatibility score

      Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


      Dependabot commands and options

      You can trigger Dependabot actions by commenting on this PR:

      • @dependabot rebase will rebase this PR
      • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
      • @dependabot merge will merge this PR after your CI passes on it
      • @dependabot squash and merge will squash and merge this PR after your CI passes on it
      • @dependabot cancel merge will cancel a previously requested merge and block automerging
      • @dependabot reopen will reopen this PR if it is closed
      • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
      • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
      • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
      • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
      • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
      • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
      • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
      • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

      You can disable automated security fix PRs for this repo from the Security Alerts page.

      dependencies 
      opened by dependabot[bot] 0
    Releases(v1.8.6)
    Owner
    Grzegorz Tomicki
    Front-end developer
    Grzegorz Tomicki
    Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.

    Select2 Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and pagination of results. To get started, ch

    Select2 25.5k Jan 1, 2023
    🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.

    downshift ?? Primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components. Read the docs | See

    Downshift 11.1k Jan 1, 2023
    typeahead.js is a fast and fully-featured autocomplete library

    typeahead.js Inspired by twitter.com's autocomplete search functionality, typeahead.js is a flexible JavaScript library that provides a strong foundat

    Twitter 16.5k Dec 31, 2022
    🔮 Fast and full-featured autocomplete library

    Autocomplete A JavaScript library that lets you quickly build autocomplete experiences ?? Autocomplete v1 is in an alpha phase and early feedback is w

    Algolia 2.3k Jan 6, 2023
    🔮 Fast and full-featured autocomplete library

    A JavaScript library that lets you quickly build autocomplete experiences All you need to get started is: A container to inject the experience into Da

    Algolia 2.3k Jan 1, 2023
    A vanilla JS customisable select box/text input plugin ⚡️

    Choices.js I'm looking for active maintainers for this project as I no longer have the time to support it. Please get in touch if you're interested ??

    Josh Johnson 5.4k Jan 7, 2023
    A platformer game using Phaser3 library and Vanilla JS. This project features the knowledge of Webpack, ES6, JS Modules, Async code, DOM, JSON and Jest tests.

    RUNNING BUNNY A platformer game using Phaser3 library and Vanilla JS. This project features the knowledge of Webpack, ES6, JS Modules, Async code, DOM

    Ana Paula Hübner 27 Mar 29, 2022
    A multi-select component with nested options support for Vue.js

    vue-treeselect A multi-select component with nested options support for Vue.js Features Single & multiple select with nested options support Fuzzy mat

    Fangzhou Li 2.6k Dec 29, 2022
    This project was developed to practice Front-end and Back-end comunication, data formatting, http requests GET, POST, DELETE, form validation, it also consumes a rest API

    React Application ?? Demonstration of the application | Features | Technologies used | Application installation ?? Demonstration of the application Ap

    Victor Lira 36 May 17, 2022
    local storage wrapper for both react-native and browser. Support size controlling, auto expiring, remote data auto syncing and getting batch data in one query.

    react-native-storage This is a local storage wrapper for both react native apps (using AsyncStorage) and web apps (using localStorage). ES6 syntax, pr

    Sunny Luo 2.9k Dec 16, 2022
    🌀 The Javacript framework for creating a portal to your data. Perfect for a single dataset or a full catalog.

    ?? Portal.JS The javascript framework for data portals ?? portal is a framework for rapidly building rich data portal frontends using a modern fronten

    Datopian 2k Dec 30, 2022
    A high-resolution local database that uses precise algorithms to easily record data in local files within a project with persistent JSON and YAML support designed to be easy to set up and use

    About A high-resolution local database that uses precise algorithms to easily record data in local files within a project with persistent JSON and YML

    Shuruhatik 5 Dec 28, 2022
    A nodejs module for local and remote Inter Process Communication with full support for Linux, Mac and Windows

    A nodejs module for local and remote Inter Process Communication with full support for Linux, Mac and Windows

    Rifa Achrinza 15 Sep 28, 2022
    🏸 A simple plugin for image zooming without dependencies ~1.65KB gzip

    ZOOOM.JS A simple plugin for image zoooming without dependencies. Only pure javascipt. Installation CDN JavaScript <script src="https://cdn.jsdelivr.n

    Grzegorz Tomicki 13 Aug 30, 2022
    A lightweight and amazing WYSIWYG JavaScript editor - 20kB only (8kB gzip)

    Supporting Trumbowyg Trumbowyg is an MIT-licensed open source project and completely free to use. However, the amount of effort needed to maintain and

    Alexandre Demode 3.8k Jan 7, 2023
    This compress library was made with Brotli and Gzip help, for React users who want to make website more performance and reduce JS bundle code

    React-compress This compress library was made with Brotli and Gzip help, for React users who want to make website more performance and reduce JS bundl

    Koma Human 30 Jan 6, 2023