etto

small javascript autocomplete and select component

full readme on github


note: etto is intended for use in modern browsers

install

npm install etto

or

<script src="https://unpkg.com/etto/dist/etto.min.js">

and (optional) styles

<link rel="stylesheet" href="https://unpkg.com/etto/dist/etto.css">

examples

input mode

choices = [
    { label: 'Alabama', value: 'Alabama' },
    { label: 'Alaska', value: 'Alaska, USA' },
    { label: 'Wyoming', value: 'WY' }
];

etto = new Etto(document.getElementById('demo-1'), {}, choices);

select mode

choices = [
    { label: 'Minnesota', value: 'Minnesota' },
    { label: 'Wyoming', value: 'Wyoming' },
    { label: 'New York', value: 'New York' }
];

etto = new Etto(document.getElementById('demo-2'), { selectMode: true }, choices);

ajax source (example using star wars api)

try searching for "darth vader".

this example uses fetch, and AbortController to abort requests that don't need to be completed. Remember to abort requests you don't intend to complete. Read more here.

let controller = null;

etto = new Etto(document.getElementById('demo-3'), {
    source: async (query, done) => {
        if (controller) controller.abort();
        controller = new AbortController();

        try {
            const choices = [];
            const response = await fetch(`https://swapi.dev/api/people/?search=${query}`, {
                signal: controller.signal
            });

            controller = null;
            const json = await response.json();
            json.results.forEach(person => {
                choices.push({ label: person.name, value: person.name })
            });

            done(choices);
        } catch(err) {
            if (err.name !== 'AbortError') {
                done([]);
                throw err;
            }
        }
    }
});

using custom properties

etto has a few customizable properties for you to use.

emptyHtml

etto = new Etto(document.getElementById('demo-4'), { emptyHtml: '<b>Nothing here chief!</b>' }, choices);

createItemFn

use the createItemFn property to tell etto how to render li items in the dropdown list. a couple of parameters are passed, which you can use to return an html string.

Note: elements must be li tags and must include a data-index attribute with the value provided by the index parameter. see below for an example.

createItemFn(choice: object, index: number, inputVal: string, isHighlighted: boolean, isSelected: boolean): string
choices = [
    { label: 'Minnesota', value: 'Minnesota' },
    { label: 'Wyoming', value: 'Wyoming' },
    { label: 'New York', value: 'New York' }
];

etto = new Etto(document.getElementById('demo-5'), {
    createItemFn: (choice, index, inputVal, isHighlighted, isSelected) => {
        return `<li class="etto-li" data-index="${index}">` +
            '<img src="https://kevinfiol.github.io/etto/img.png" />' +
            `<b>label: ${choice.label}, value: ${choice.value}</b>` +
        '</li>';
    }
}, choices);

filterFn

use the filterFn property to tell etto how to filter the choices list.

filterFn(inputVal: string, choices: array, matchFullWord: boolean, maxResults: number): array

This example uses a filter function that only matches against choice.value, and does not consider choice.label

choices = [
    { label: 'banana', value: 'yellow' },
    { label: 'apple', value: 'red' },
    { label: 'kiwi', value: 'green' }
];

etto = new Etto(document.getElementById('demo-6'), {
    filterFn: (inputVal, choices, matchFullWord, maxResults) => {
        const v = inputVal.toUpperCase();

        return choices.filter(choice => {
            return choice.value.toUpperCase().indexOf(v) > -1;
        });
    }
}, choices);

onSelect & onClear

pass a callback function as onSelect to tell etto to trigger something upon selecting a choice. similarly, use onClear to trigger an event upon programmatically clearing the input.

None selected.
choices = [
    { label: 'The Legend of Zelda', value: 'The Legend of Zelda', year: 1986 },
    { label: 'Yoshi\'s Island', value: 'Yoshi\'s Island', year: 1995 },
    { label: 'Animal Crossing', value: 'Animal Crossing', year: 2002 }
];

const box = document.getElementById('demo-7-box');

etto = new Etto(document.getElementById('demo-7'), {
    selectMode: true,
    onSelect: choice => {
        box.innerText = `Selected: ${choice.label} was released in ${choice.year}.`;
    },
    onClear: () => {
        box.innerText = 'None selected.';
    }
}, choices);

onValue

you can also use the onValue callback which triggers after every new value to the input element.

Waiting for input...
choices = [
    { label: 'Apples' },
    { label: 'Bananas' },
    { label: 'Animal Crossing' }
];

const demo8box = document.getElementById('demo-8-box');

etto = new Etto(document.getElementById('demo-8'), {
    onValue: value => {
        demo8box.innerText = value.trim() ? value : 'Input is currently empty.';
    }
}, choices);