Autocomplete

Minimalist autocomplete with really fast lookup.

by phloe

HTML

<p class="suggest">
    <input class="user" value="" type="text" autofocus/>
    <span><b></b><em></em><i>no match</i></span>
</p>

SCSS

@font-face {
    font-family: "TitilliumText22LThin";
    src: url("http://phloe.net/base/font/TitilliumText22L001-webfont.eot?#iefix") format("embedded-opentype"), url("http://phloe.net/base/font/TitilliumText22L001-webfont.woff") format("woff"), url("http://phloe.net/base/font/TitilliumText22L001-webfont.ttf") format("truetype"), url("http://phloe.net/base/font/TitilliumText22L001-webfont.svg#@{@font-face}") format("svg");
}

* {
    padding: 0;
    margin: 0;
}

body {
    background-color: #EEE;
}

.suggest {
    position: relative;
    background-color: #FFF;
    font: normal 20px/normal "TitilliumText22LThin", sans-serif;
    
    input,
    span {
        display: block;        
        -webkit-box-sizing: border-box;
        -moz-box-sizing: border-box;
        -ms-box-sizing: border-box;
        -o-box-sizing: border-box;
        box-sizing: border-box;
        font: inherit;
        padding: 0;
        border: 0 solid transparent;
        vertical-align: top;
        text-align: left;
        padding: 2px 5px;
    }

    input {
        position: relative;
        width: 100%;
        z-index: 2;
        background-color: transparent;
        color: #222;
    }
    
    span {     
        position: absolute;
        z-index: 1;
        top: 0;
        width: 100%;
    }
    
    em, b, i {
        color: #08c8ff;
        font: inherit;
    }
    
    b {
        color: transparent;
    }
    
    i {
        position: absolute;
        padding: 2px 5px;
        top: 0;
        right: 0;
    }
}

html>/**/body input, x:-moz-any-link, x:default { text-indent:-1px; }

JavaScript

var Dictionary = (function () {

    var dict = function (words) {
    
        this.tree = {
            start: 0,
            end: 0        
        };
        
        this.words = [];
        
        if (words && words.length) {
            addWords.call(this, words);
        }
    };
    
    // public method
    dict.prototype.getMatches = function (word) {
        var matches = [],
            branch = this.tree,
            chars = word.toLowerCase().split(""),
            l = chars.length,
            i = 0,
            char;
    
        while (i < l && branch) {
            char = chars[i++];
            branch = (char in branch) ? branch[char] : false;
        }
        
        if (branch) {
            matches = this.words.slice(branch.start, branch.end + 1);
        }
        
        return matches;
    };
    
    // private pseudo method
    function addWord (word) {
        var index = this.words.push(word) - 1;
        
        var branch = this.tree,
            chars = word.split(""),
            l = chars.length,
            i = 0,
            char;
        
        if (branch.start > index) {
            branch.start = index;
        }
        else if (branch.end < index) {
            branch.end = index;
        }
        
        while (i < l) {
            char = chars[i++];
            if (!(char in branch)) {
                branch[char] = {
                    start: index,
                    end: index           
                };
            }
            branch = branch[char];
            if (branch.start > index) {
                branch.start = index;
            }
            else if (branch.end < index) {
                branch.end = index;
            }
        }
    }
    
    // private pseudo method
    function addWords (words) {
        words = words.slice(0);
        var l = words.length,
            i = 0;
        while (i < l) {
            words[i] = words[i++].toLowerCase();
        }
        
       ...