Dynamic, instant autocompletion via datalist

by rudiedirkx

HTML

<p>Javascript hack: Search for "r", "ru", "rud", "rudi" and "rudo":</p>
<p>
  <input list="suggestions" autocomplete="off" autofocus="1" />
  <datalist id="suggestions"></datalist>
</p>
<p>
  <label><input type="checkbox" id="clean" /> Remove options while searching</label>
  <label><input type="checkbox" id="slow" /> Searching is slow</label>
</p>

<p>Normal autocomplete:</p>
<p>
  <input list="suggestions2" autocomplete="off" />
  <datalist id="suggestions2"><option value="ralph"><option value="ronny"><option value="rudie"><option value="rutte"><option value="rudiedirkx"><option value="rudolf"></datalist>
</p>

CSS

label:after {
    content: "\A";
    white-space: pre;
}

JavaScript

var input = document.querySelector('input'),
    datalist = document.querySelector('datalist');
var clean = document.getElementById('clean'),
    slow = document.getElementById('slow');

// Show options to user
function setOptions(options) {
    datalist.innerHTML = options.map(function(o) {
        return '<option value="' + o + '" />';
    }).join();
}

// Async fetch matches
function findOptions(search, callback) {
    var respond = function(options) {
        setTimeout(function() {
            callback(options);
        }, slow.checked ? 350 : 100);
    };
    switch (search) {
        case 'r':
            respond(['ralph', 'ronny', 'rudie']);
            break;
        case 'ru':
            respond(['rudie', 'rutte', 'rudiedirkx']);
            break;
        case 'rud':
            respond(['rudie', 'rudiedirkx', 'rudolf']);
            break;
        case 'rudi':
            respond(['rudie']);
            break;
        case 'rudo':
            respond(['rudolf']);
            break;
        default:
            respond([]);
    }
}

// ondirectsearch, instant, no delay
input.addEventListener('keyup', function(e) {
    var lastValue = this.lastValue,
        thisValue = this.value;
    if (lastValue != thisValue) {
        this.lastValue = thisValue;
        
        // Empty autocomplete
        if (clean.checked) {
            setOptions([]);
        }
        
        // Async find matches
        findOptions(thisValue, function(options) {
            // Re-fill autocomplete
            setOptions(options);
        });
    }
});