Swiftype Auto Suggest

Google-like auto suggester

by ninty9notout

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div class="swiftype-autosuggest">
    <div class="swiftype-autosuggest__control">
        <input type="text" class="swiftype-autosuggest__input" placeholder="e.g. dudamel">
        <button type="submit" class="swiftype-autosuggest__button">Search</button>
    </div>
    <div class="swiftype-autosuggest__results">
        <ul class="swiftype-autosuggest__list">
            <!-- intentionally empty -->
        </ul>
    </div>
</div>

SCSS

body {
    background: #34495e;
    font-size: 16px;
}

.swiftype-autosuggest {
    background: #2c3e50;
    padding: 2.5rem;
    position: relative;
}

.swiftype-autosuggest__control {
    font-size: 0;
    display: flex;
    flex-direction: row;
    border-radius: 0.25rem;
    overflow: hidden;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
}

.swiftype-autosuggest__input,
.swiftype-autosuggest__button {
    font-size: 16px;
    padding: 1rem;
    border: 0;
    margin: 0;
}

.swiftype-autosuggest__input {
    flex: 1;
}

.swiftype-autosuggest__button {
    background: #3498db;
    color: #ffffff;
}

.swiftype-autosuggest__input:focus,
.swiftype-autosuggest__button:focus {
    outline: none;
}

.swiftype-autosuggest__results {
    background: #ffffff;
    position: absolute;
    width: calc(100% - 5rem);
    border-radius: 0 0 0.25rem 0.25rem;
    overflow: hidden;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
    border-top: 1px solid #ecf0f1;
    display: none;
    font-size: 0;
}

.swiftype-autosuggest__list {
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 0;
}

.swiftype-autosuggest__link {
    padding: 1rem;
    display: block;
    font-size: 1.125rem;
    text-decoration: none;
    color: #000000;
}

.swiftype-autosuggest--active {
    .swiftype-autosuggest__control {
        border-radius: 0.25rem 0.25rem 0 0;
    }

    .swiftype-autosuggest__results {
        display: initial;
    }
}

.swiftype-autosuggest__item--active {
    .swiftype-autosuggest__link {
        background: #1abc9c;
        color: #ffffff;
    }
}

JavaScript

const inputDebounce = 500;
const minLength = 2;
const maxSuggestions = 10;
const engineKey = 'ZLUGCACvKay36iYNb5je';
const urlPrefix = '/search?q=';
const fields = ['sections'];

const selectors = {
	container: '.swiftype-autosuggest',
    input: '.swiftype-autosuggest__input',
    list: '.swiftype-autosuggest__list',
    item: '.swiftype-autosuggest__item',
    itemActive: '.swiftype-autosuggest__item--active',
    link: '.swiftype-autosuggest__link',
}

const classes = {
	containerActive: 'swiftype-autosuggest--active',
    item: 'swiftype-autosuggest__item',
    itemActive: 'swiftype-autosuggest__item--active',
    link: 'swiftype-autosuggest__link',
}

const $container = document.querySelector(selectors.container);
const $input = document.querySelector(selectors.input);
const $list = document.querySelector(selectors.list);

const cache = {}

const keys = {
	up: 38,
	down: 40,
}

let currentQuery = null;
let totalItems = null;
let currentItem = null;

const isString = (value) => {
	return Object.prototype.toString.call(value) === '[object String]';
}

const debounce = (callback, delay) => {
    let timeout;

    return function(...args) {
        clearTimeout(timeout);

        timeout = setTimeout(() => {
        	callback.apply(this, args);
        }, delay);
    }
}

const resetSuggestions = () => {
    currentQuery = null;
    totalItems = null;
    currentItem = null;

	hideSuggestions();
}

const decode = (value) => {
    const decoder = document.createElement('textarea');

    decoder.innerHTML = value;

    if (decoder.childNodes.length === 0) {
    	return '';
    }

	return decoder.childNodes[0].nodeValue;
}

const getSuggestions = (query) => {
	const params = new URLSearchParams({
    	q: query,
        engine_key: engineKey,
        spelling: 'always',
        per_page: 50,
    });

	const url = 'https://api.swiftype.com/api/v1/public/engines/search.json?' + params.toString();

	return axios.get(url).then((response) => {
    	let suggestions =...