Skip to content

Instantly share code, notes, and snippets.

@siygle
Last active December 17, 2015 00:19
Show Gist options
  • Select an option

  • Save siygle/5520375 to your computer and use it in GitHub Desktop.

Select an option

Save siygle/5520375 to your computer and use it in GitHub Desktop.
Typeahead Example
<!doctype html>
<html>
<head>
<title>AutoComplete Example</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<form>
<!-- Don't forget disable chrome autocomplete -->
<input type="text" id="moz-test" autocomplete="off">
<button type="submit" disabled>Submit</button>
</form>
<script type="text/javascript" src="script.js"></script>
<script>
// load module first then initial it as following
// Typeahead(elementId, [options])
// Options:
// @source - Fetch remote url [string] || Local dataset [array]
// @cache [number] - how long will we keep in localStorage, default would be 1hr.
var ele = new Typeahead('moz-test', {
//cache: 3600000,
source: 'tz.json'
});
</script>
</body>
</html>
'use strict';
(function (global) {
var matches =
document.documentElement.matchesSelector ||
document.documentElement.webkitMatchesSelector ||
document.documentElement.mozMatchesSelector;
var insertAfter = function(newEl, target){
var parent = target.parentNode;
if (parent.lastChild == target) {
parent.appenChild(newEl);
} else {
parent.insertBefore(newEl, target.nextSibling);
}
}
var loadJSON = function(path, cb, scope) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var data = JSON.parse(httpRequest.responseText);
if (cb) cb.call(scope||this, data);
}
}
}
httpRequest.open('GET', path);
httpRequest.send();
}
var checkStorage = function() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch(e) {
return false;
}
}
var loadLocal = function() {
if (!checkStorage()) return false;
var storage = window.localStorage;
var source = storage.getItem('typeahead-dataset');
if (source) {
var expire = storage.getItem('typeahead-expire');
var now = new Date().getTime();
if (expire && now < expire) {
return source;
}
}
return false;
}
var saveLocal = function(data, expire) {
if (!checkStorage()) return false;
var storage = window.localStorage;
var now = new Date().getTime();
var expire = (expire) ? (now + expire) : (now + 3600000);
storage.setItem('typeahead-dataset', JSON.stringify(data));
storage.setItem('typeahead-expire', expire);
}
var Typeahead = function(el, options) {
if (typeof el === 'string') {
el = document.getElementById('moz-test');
}
if (!options.source) {
throw new Error("Miss data source");
} else {
if (typeof options.source === 'string') {
// If local cache exist then use it!
var local = loadLocal();
if (local) {
this.source = JSON.parse(local).source;
} else {
loadJSON(options.source, function(data) {
this.source = data.source;
// Save remote dataset to localStorage
var dataset = { source: data.source };
saveLocal(dataset, options.expire);
}, this);
}
} else if (options.source instanceof Array) {
this.source = source;
} else {
throw new Error("Wrong data source format");
}
}
this.element = el;
this.shown = false;
this.valid = false;
this.menu();
this.button();
this.listen();
}
Typeahead.prototype = {
constructor: Typeahead,
menu: function() {
var xinit = this.element.offsetLeft;
var yinit = this.element.offsetTop;
this.menu = document.createElement('ul');
this.menu.className = 'dropdown';
this.menu.setAttribute('style', 'left:' + xinit + 'px;top:' + yinit + 'px;');
insertAfter(this.menu, this.element);
},
button: function() {
var parent = this.element.parentNode;
var el = parent.querySelector('button[type=submit]');
if (el) this.button = el;
},
select: function() {
var li = this.menu.querySelector('.active');
if (li) {
var val = this.menu.querySelector('.active').textContent;
this.element.value = val;
this.button.disabled = false;
this.hide();
}
},
hide: function() {
this.menu.style.display = 'none';
this.shown = false;
return this;
},
render: function(match){
var content = '';
match.forEach(function(item){
content += '<li>' + item + '</li>';
});
this.menu.innerHTML = content;
this.menu.style.display = 'block';
this.shown = true;
},
matcher: function(query){
var match = [];
this.source.forEach(function(item){
(item.indexOf(query) !== -1) ? match.push(item) : '';
});
if (match.length > 0) {
this.render(match);
} else {
if (this.shown) this.hide();
}
},
lookup: function(){
var query = this.element.value;
if (query && query.length > 0){
this.matcher(query);
}
},
up: function() {
var el = this.menu.querySelector('.active');
var items = this.menu.getElementsByTagName('li');
var prev;
if (!el) {
prev = items[items.length - 1];
} else {
el.className = el.className.replace('active', '');
prev = el.previousSibling;
if (!prev) {
prev = items[items.length - 1];
}
}
prev.className = 'active';
},
down: function() {
var el = this.menu.querySelector('.active');
var items = this.menu.getElementsByTagName('li');
var next;
if (!el) {
next = items[0];
} else {
el.className = el.className.replace('active', '');
next = el.nextSibling;
if (!next) {
next = items[0];
}
}
next.className = 'active';
},
keydown: function(event) {
switch(event.keyCode) {
case 13:
event.preventDefault();
break;
case 38:
if (!this.shown) return;
this.up();
break;
case 40:
if (!this.shown) return;
this.down();
break;
}
if (!this.button.disabled) {
this.button.disabled = true;
}
},
keyup: function(event){
event.stopPropagation();
event.preventDefault();
switch(event.keyCode) {
case 40:
case 38:
case 16:
case 17:
case 18:
break;
case 13:
if (!this.shown) return;
this.select();
break;
case 27:
if (!this.shown) return;
this.hide();
break;
default:
this.lookup();
}
},
mouseover: function(event){
var target = event.target;
if (matches.call(target, 'ul li')) {
var active = this.menu.querySelector('.active');
if (active) {
active.className = active.className.replace('active', '');
}
target.className = 'active';
}
this.inMenu = true;
},
click: function(event) {
if (!this.shown) return;
this.select();
event.stopPropagation();
event.preventDefault();
},
listen: function(){
var self = this;
this.element.addEventListener("keydown", this.keydown.bind(this), false);
this.element.addEventListener("keyup", this.keyup.bind(this), false);
this.element.addEventListener("blur", function(event){
if (!self.inMenu && self.shown) {
self.hide();
}
});
this.menu.addEventListener("click", this.click.bind(this), false);
this.menu.addEventListener("mouseover", this.mouseover.bind(this), false);
this.menu.addEventListener("mouseout", function(event){
self.inMenu = false;
} ,false)
}
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = Typeahead;
} else if (typeof define === 'function' && define.amd) {
define('typeahead', [], function() { return Typeahead; })
} else {
global.Typeahead = Typeahead;
}
})(this);
.dropdown {
display: none;
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
list-style: none;
background-color: #666;
min-width: 150px;
min-height: 100px;
}
.dropdown .active {
background-color: #999;
}
{
"source": [
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
"Africa/Algiers",
"Africa/Asmara",
"Africa/Bamako",
"Africa/Bangui",
"Africa/Banjul",
"Africa/Bissau",
"Africa/Blantyre",
"Africa/Brazzaville",
"Africa/Bujumbura",
"Africa/Cairo",
"Africa/Casablanca",
"Africa/Ceuta",
"Africa/Conakry",
"Africa/Dakar",
"Africa/Dar_es_Salaam",
"Africa/Djibouti",
"Africa/Douala",
"Africa/El_Aaiun",
"Africa/Freetown",
"Africa/Gaborone",
"Africa/Harare",
"Africa/Johannesburg",
"Africa/Juba",
"Africa/Kampala",
"Africa/Khartoum",
"Africa/Kigali",
"Africa/Kinshasa",
"Africa/Lagos",
"Africa/Libreville",
"Africa/Lome",
"Africa/Luanda",
"Africa/Lubumbashi",
"Africa/Lusaka",
"Africa/Malabo",
"Africa/Maputo",
"Africa/Maseru",
"Africa/Mbabane",
"Africa/Mogadishu",
"Africa/Monrovia",
"Africa/Nairobi",
"Africa/Ndjamena",
"Africa/Niamey",
"Africa/Nouakchott",
"Africa/Ouagadougou",
"Africa/Porto-Novo",
"Africa/Sao_Tome",
"Africa/Timbuktu",
"Africa/Tripoli",
"Africa/Tunis",
"Africa/Windhoek",
"America/Adak",
"America/Anchorage",
"America/Anguilla",
"America/Antigua",
"America/Araguaina",
"America/Argentina/Buenos_Aires",
"America/Argentina/Catamarca",
"America/Argentina/Comodoro_Rivadavia",
"America/Argentina/Cordoba",
"America/Argentina/Jujuy",
"America/Argentina/La_Rioja",
"America/Argentina/Mendoza",
"America/Argentina/Rio_Gallegos",
"America/Argentina/Salta",
"America/Argentina/San_Juan",
"America/Argentina/San_Luis",
"America/Argentina/Tucuman",
"America/Argentina/Ushuaia",
"America/Aruba",
"America/Asuncion",
"America/Atikokan",
"America/Atka",
"America/Bahia",
"America/Bahia_Banderas",
"America/Barbados",
"America/Belem",
"America/Belize",
"America/Blanc-Sablon",
"America/Boa_Vista",
"America/Bogota",
"America/Boise",
"America/Brasilia",
"America/Cambridge_Bay",
"America/Campo_Grande",
"America/Cancun",
"America/Caracas",
"America/Cayenne",
"America/Cayman",
"America/Chicago",
"America/Chihuahua",
"America/Coral_Harbour",
"America/Costa_Rica",
"America/Creston",
"America/Cuiaba",
"America/Curacao",
"America/Danmarkshavn",
"America/Dawson",
"America/Dawson_Creek",
"America/Denver",
"America/Detroit",
"America/Dominica",
"America/Edmonton",
"America/Eirunepe",
"America/El_Salvador",
"America/Ensenada",
"America/Fort_Wayne",
"America/Fortaleza",
"America/Glace_Bay",
"America/Godthab",
"America/Goose_Bay",
"America/Grand_Turk",
"America/Grenada",
"America/Guadeloupe",
"America/Guatemala",
"America/Guayaquil",
"America/Guyana",
"America/Halifax",
"America/Havana",
"America/Hermosillo",
"America/Indiana/Indianapolis",
"America/Indiana/Knox",
"America/Indiana/Marengo",
"America/Indiana/Petersburg",
"America/Indiana/Tell_City",
"America/Indiana/Vevay",
"America/Indiana/Vincennes",
"America/Indiana/Winamac",
"America/Indianapolis",
"America/Inuvik",
"America/Iqaluit",
"America/Jamaica",
"America/Juneau",
"America/Kentucky/Louisville",
"America/Kentucky/Monticello",
"America/Knox_IN",
"America/Kralendijk",
"America/La_Paz",
"America/Lima",
"America/Los_Angeles",
"America/Louisville",
"America/Lower_Princes",
"America/Maceio",
"America/Managua",
"America/Manaus",
"America/Marigot",
"America/Martinique",
"America/Matamoros",
"America/Mazatlan",
"America/Menominee",
"America/Merida",
"America/Metlakatla",
"America/Mexico_City",
"America/Miquelon",
"America/Moncton",
"America/Monterrey",
"America/Montevideo",
"America/Montreal",
"America/Montserrat",
"America/Nassau",
"America/New_York",
"America/Nipigon",
"America/Nome",
"America/Noronha",
"America/North_Dakota/Beulah",
"America/North_Dakota/Center",
"America/North_Dakota/New_Salem",
"America/Ojinaga",
"America/Panama",
"America/Pangnirtung",
"America/Paramaribo",
"America/Phoenix",
"America/Port-au-Prince",
"America/Port_of_Spain",
"America/Porto_Acre",
"America/Porto_Velho",
"America/Puerto_Rico",
"America/Rainy_River",
"America/Rankin_Inlet",
"America/Recife",
"America/Regina",
"America/Resolute",
"America/Rio_Branco",
"America/Rosario",
"America/Santa_Isabel",
"America/Santarem",
"America/Santiago",
"America/Santo_Domingo",
"America/Sao_Paulo",
"America/Scoresbysund",
"America/Shiprock",
"America/Sitka",
"America/St_Barthelemy",
"America/St_Johns",
"America/St_Kitts",
"America/St_Lucia",
"America/St_Thomas",
"America/St_Vincent",
"America/Swift_Current",
"America/Tegucigalpa",
"America/Thule",
"America/Thunder_Bay",
"America/Tijuana",
"America/Toronto",
"America/Tortola",
"America/Vancouver",
"America/Virgin",
"America/Whitehorse",
"America/Winnipeg",
"America/Yakutat",
"America/Yellowknife",
"Antarctica/Casey",
"Antarctica/Davis",
"Antarctica/DumontDUrville",
"Antarctica/Macquarie",
"Antarctica/Mawson",
"Antarctica/McMurdo",
"Antarctica/Palmer",
"Antarctica/Rothera",
"Antarctica/South_Pole",
"Antarctica/Syowa",
"Antarctica/Vostok",
"Asia/Aden",
"Asia/Almaty",
"Asia/Amman",
"Asia/Anadyr",
"Asia/Aqtau",
"Asia/Aqtobe",
"Asia/Ashgabat",
"Asia/Ashkhabad",
"Asia/Baghdad",
"Asia/Bahrain",
"Asia/Baku",
"Asia/Bangkok",
"Asia/Beirut",
"Asia/Bishkek",
"Asia/Brunei",
"Asia/Choibalsan",
"Asia/Chongqing",
"Asia/Chungking",
"Asia/Colombo",
"Asia/Damascus",
"Asia/Dhaka",
"Asia/Dili",
"Asia/Dubai",
"Asia/Dushanbe",
"Asia/Gaza",
"Asia/Harbin",
"Asia/Hebron",
"Asia/Ho_Chi_Minh",
"Asia/Hong_Kong",
"Asia/Hovd",
"Asia/Irkutsk",
"Asia/Istanbul",
"Asia/Jakarta",
"Asia/Jayapura",
"Asia/Jerusalem",
"Asia/Kabul",
"Asia/Kamchatka",
"Asia/Karachi",
"Asia/Kashgar",
"Asia/Kathmandu",
"Asia/Kolkata",
"Asia/Krasnoyarsk",
"Asia/Kuala_Lumpur",
"Asia/Kuching",
"Asia/Kuwait",
"Asia/Macau",
"Asia/Magadan",
"Asia/Makassar",
"Asia/Manila",
"Asia/Muscat",
"Asia/Nicosia",
"Asia/Novokuznetsk",
"Asia/Novosibirsk",
"Asia/Omsk",
"Asia/Oral",
"Asia/Phnom_Penh",
"Asia/Pontianak",
"Asia/Pyongyang",
"Asia/Qatar",
"Asia/Qyzylorda",
"Asia/Rangoon",
"Asia/Riyadh",
"Asia/Saigon",
"Asia/Sakhalin",
"Asia/Samarkand",
"Asia/Seoul",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Taipei",
"Asia/Tashkent",
"Asia/Tbilisi",
"Asia/Tehran",
"Asia/Tel_Aviv",
"Asia/Thimphu",
"Asia/Tokyo",
"Asia/Ulan_Bator",
"Asia/Urumqi",
"Asia/Vientiane",
"Asia/Vladivostok",
"Asia/Yakutsk",
"Asia/Yekaterinburg",
"Asia/Yerevan",
"Atlantic/Azores",
"Atlantic/Bermuda",
"Atlantic/Canary",
"Atlantic/Cape_Verde",
"Atlantic/Faroe_Islands",
"Atlantic/Jan_Mayen",
"Atlantic/Madeira",
"Atlantic/Reykjavik",
"Atlantic/South_Georgia",
"Atlantic/St_Helena",
"Atlantic/Stanley",
"Australia/Adelaide",
"Australia/Brisbane",
"Australia/Broken_Hill",
"Australia/Currie",
"Australia/Darwin",
"Australia/Eucla",
"Australia/Hobart",
"Australia/Lindeman",
"Australia/Lord_Howe",
"Australia/Melbourne",
"Australia/Perth",
"Australia/Sydney",
"Europe/Amsterdam",
"Europe/Andorra",
"Europe/Athens",
"Europe/Belfast",
"Europe/Belgrade",
"Europe/Berlin",
"Europe/Bratislava",
"Europe/Brussels",
"Europe/Bucharest",
"Europe/Budapest",
"Europe/Chisinau",
"Europe/Copenhagen",
"Europe/Dublin",
"Europe/Gibraltar",
"Europe/Guernsey",
"Europe/Helsinki",
"Europe/Isle_of_Man",
"Europe/Istanbul",
"Europe/Jersey",
"Europe/Kaliningrad",
"Europe/Kiev",
"Europe/Lisbon",
"Europe/Ljubljana",
"Europe/London",
"Europe/Luxembourg",
"Europe/Madrid",
"Europe/Malta",
"Europe/Mariehamn",
"Europe/Minsk",
"Europe/Monaco",
"Europe/Moscow",
"Europe/Nicosia",
"Europe/Oslo",
"Europe/Paris",
"Europe/Podgorica",
"Europe/Prague",
"Europe/Riga",
"Europe/Rome",
"Europe/Samara",
"Europe/San_Marino",
"Europe/Sarajevo",
"Europe/Simferopol",
"Europe/Skopje",
"Europe/Sofia",
"Europe/Stockholm",
"Europe/Tallinn",
"Europe/Tirane",
"Europe/Tiraspol",
"Europe/Uzhgorod",
"Europe/Vaduz",
"Europe/Vatican",
"Europe/Vienna",
"Europe/Vilnius",
"Europe/Volgograd",
"Europe/Warsaw",
"Europe/Zagreb",
"Europe/Zaporozhye",
"Europe/Zurich",
"Indian/Antananarivo",
"Indian/Chagos",
"Indian/Christmas",
"Indian/Cocos",
"Indian/Comoro",
"Indian/Kerguelen",
"Indian/Mahe",
"Indian/Maldives",
"Indian/Mauritius",
"Indian/Mayotte",
"Indian/Reunion",
"Pacific/Apia",
"Pacific/Auckland",
"Pacific/Chatham",
"Pacific/Chuuk_Lagoon",
"Pacific/Easter",
"Pacific/Efate",
"Pacific/Enderbury",
"Pacific/Fakaofo",
"Pacific/Fiji",
"Pacific/Funafuti",
"Pacific/Galapagos",
"Pacific/Gambier",
"Pacific/Guadalcanal",
"Pacific/Guam",
"Pacific/Honolulu",
"Pacific/Johnston",
"Pacific/Kiritimati",
"Pacific/Kosrae",
"Pacific/Kwajalein",
"Pacific/Majuro",
"Pacific/Marquesas",
"Pacific/Midway",
"Pacific/Nauru",
"Pacific/Niue",
"Pacific/Norfolk",
"Pacific/Noumea",
"Pacific/Pago_Pago",
"Pacific/Palau",
"Pacific/Pitcairn",
"Pacific/Pohnpei",
"Pacific/Ponape",
"Pacific/Port_Moresby",
"Pacific/Rarotonga",
"Pacific/Saipan",
"Pacific/Samoa",
"Pacific/Tahiti",
"Pacific/Tarawa",
"Pacific/Tongatapu",
"Pacific/Wake",
"Pacific/Wallis",
"Pacific/Yap"
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment