Last active
December 30, 2021 06:48
-
-
Save mjy9088/ac3a033735d2d904cec08c76cebb6d33 to your computer and use it in GitHub Desktop.
Input autocompletion
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var InputAutocomplete = window.InputAutocomplete = (function () { | |
function result(options) { | |
if (!(this instanceof result)) throw new Error('InputAutocomplete is a constructor'); | |
if (!options) throw new Error('InputAutocomplete requires options to be provided'); | |
this.inputElement = options.inputElement; | |
this.show = options.show; | |
this.hide = options.hide; | |
this.setFailed = options.failed; | |
this.setLoading = options.loading; | |
this.setPlaceholder = options.placeholder; | |
this.listElement = options.listElement; | |
this.insertBefore = options.insertBefore || null; | |
this.dataLoader = options.dataLoader; | |
this.renderer = options.renderer; | |
this.stopped = true; | |
this.changeListener = this.change.bind(this); | |
this.focusListener = this.focus.bind(this); | |
this.blurListener = this.blur.bind(this); | |
this.loading = false; | |
this.pending = false; | |
this.data = []; | |
this.elementPool = []; | |
this.showingElements = []; | |
} | |
Object.defineProperties(result.prototype, { | |
start: { | |
value: function () { | |
if (!this.stopped) return; | |
this.stopped = false; | |
this.inputElement.addEventListener('keyup', this.changeListener); | |
this.inputElement.addEventListener('focus', this.focusListener); | |
this.inputElement.addEventListener('blur', this.blurListener); | |
this.change(); | |
if (this.inputElement == document.activeElement) { | |
this.show(); | |
} else { | |
this.hide(); | |
} | |
} | |
}, | |
stop: { | |
value: function () { | |
if (this.stopped) return; | |
this.stopped = true; | |
this.inputElement.removeEventListener('keyup', this.changeListener); | |
this.inputElement.removeEventListener('focus', this.focusListener); | |
this.inputElement.removeEventListener('blur', this.blurListener); | |
this.hide(); | |
} | |
}, | |
change: { | |
value: function () { | |
if (this.loading) { | |
this.pending = true; | |
return; | |
} else { | |
this.loading = true; | |
this.setLoading(true); | |
} | |
var self = this; | |
this.dataLoader(this.inputElement.value, function (data) { | |
self.loading = false; | |
if (self.pending) { | |
self.change(); | |
} else { | |
self.setLoading(false); | |
} | |
self.pending = false; | |
if (!data) { | |
self.setFailed(true); | |
return; | |
} | |
self.setFailed(false); | |
self.data = data; | |
self.refresh(); | |
}); | |
} | |
}, | |
refresh: { | |
value: function () { | |
this.setPlaceholder(this.data.length == 0); | |
while (this.showingElements[this.data.length]) { | |
var element = this.showingElements.pop(); | |
this.listElement.removeChild(element); | |
this.releaseElement(element); | |
} | |
for (var i = 0; i < this.data.length; i++) { | |
if (this.showingElements[i]) { | |
this.renderer(this.data[i], this.showingElements[i]); | |
} else { | |
this.showingElements[i] = this.acquireElement(this.data[i]); | |
this.listElement.insertBefore(this.showingElements[i], this.insertBefore); | |
} | |
} | |
} | |
}, | |
focus: { | |
value: function () { | |
this.show(); | |
} | |
}, | |
blur: { | |
value: function () { | |
this.hide(); | |
} | |
}, | |
acquireElement: { | |
value: function (data) { | |
return this.elementPool.length ? this.renderer(data, this.elementPool.pop()) : this.renderer(data); | |
} | |
}, | |
releaseElement: { | |
value: function (element) { | |
this.elementPool.push(element); | |
} | |
} | |
}); | |
return result; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.addEventListener('DOMContentLoaded', function () { | |
function dataLoader(text, callback) { | |
var length = Math.max(Math.floor(Math.pow(Math.abs(text.split('').reduce(function (acc, curr) { | |
var result = (acc << 5) - acc + curr.charCodeAt(0); | |
return result & result; | |
}, 0)), 0.123)) - 5, 0); | |
setTimeout(function () { | |
callback(Array.from(new Array(length)).map(function (_, i) { | |
return { | |
name: text + (i ? ' ' + (i + 1) : ''), | |
id: i | |
}; | |
})); | |
}, 250); | |
} | |
function renderer(data, recycle) { | |
var result = recycle; | |
if (!result) { | |
result = document.createElement('li'); | |
} | |
result.innerText = data.name; | |
return result; | |
} | |
var input = document.getElementById('input-autocomplete-input'); | |
var toggle = document.getElementById('input-autocomplete-toggle'); | |
var list = document.getElementById('input-autocomplete-list'); | |
var loading = document.getElementById('input-autocomplete-loading'); | |
var failed = document.getElementById('input-autocomplete-failed'); | |
var placeholder = document.getElementById('input-autocomplete-placeholder'); | |
list.addEventListener('click', function(e) { | |
if (e.target.parentElement == list) { | |
input.value = e.target.innerText; | |
} | |
}); | |
new InputAutocomplete({ | |
inputElement: input, | |
listElement: list, | |
show: function () { toggle.style.display = 'block'; }, | |
hide: function () { setTimeout(function () { toggle.style.display = ''; }, 333); }, | |
loading: function (b) { loading.style.display = b ? 'block' : ''; }, | |
failed: function (b) { failed.style.display = b ? 'block' : ''; }, | |
placeholder: function (b) { placeholder.style.display = b ? 'block' : ''; }, | |
dataLoader: dataLoader, | |
renderer: renderer | |
}).start(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html, body { | |
width: 100%; | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
border: none; | |
box-sizing: border-box; | |
} | |
header, footer { | |
height: 720px; | |
background-color: #444444; | |
} | |
.test { | |
padding: 24px; | |
} | |
#input-autocomplete-input { | |
box-sizing: border-box; | |
width: 100%; | |
height: 36px; | |
line-height: 24px; | |
padding: 5px; | |
border: 1px solid #000000; | |
border-radius: 6px; | |
} | |
#input-autocomplete-toggle { | |
display: none; | |
position: relative; | |
z-index: 1; | |
height: 0; | |
overflow: visible; | |
} | |
#input-autocomplete-list { | |
box-sizing: border-box; | |
width: 100%; | |
background: #FFFFFF; | |
margin: 0; | |
padding: 5px; | |
border: 1px solid #000000; | |
border-radius: 6px; | |
} | |
#input-autocomplete-list li { | |
list-style: none; | |
box-sizing: border-box; | |
width: 100%; | |
padding: 0 6px; | |
cursor: pointer; | |
line-height: 24px; | |
/**/ | |
min-height: 24px; | |
/*/ | |
height: 24px; | |
white-space: pre; | |
text-overflow: ellipsis; | |
/**/ | |
} | |
#input-autocomplete-list li:hover { | |
background-color: #FFFFBB; | |
} | |
#input-autocomplete-list #input-autocomplete-loading, | |
#input-autocomplete-list #input-autocomplete-failed, | |
#input-autocomplete-list #input-autocomplete-placeholder { | |
display: none; | |
cursor: default; | |
background-color: #DDDDDD; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Input Autocomplete Test</title> | |
<link rel="stylesheet" href="./style.css" /> | |
<script src="./script.js"></script> | |
<script src="./input-autocomplete.js"></script> | |
</head> | |
<body> | |
<header> | |
HEADER | |
</header> | |
<main> | |
MAIN | |
<section class="test"> | |
<input type="text" id="input-autocomplete-input" autocomplete="off" /> | |
<div id="input-autocomplete-toggle"> | |
<ul id="input-autocomplete-list"> | |
<li id="input-autocomplete-loading">Loading...</li> | |
<li id="input-autocomplete-failed">Failed to load</li> | |
<li id="input-autocomplete-placeholder">No data matching the text</li> | |
</ul> | |
</div> | |
</section> | |
</main> | |
<footer> | |
FOOTER | |
</footer> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment