jQuery autocomplete
WIP
by dumptyd
HTML
<link rel="stylesheet" href="https://bootswatch.com/cosmo/bootstrap.min.css">
<div class="row gutter-top-50">
<div class="col-xs-2"></div>
<div class="col-xs-8">
<input id="auto-field-1" placeholder="Enter text" type="text" class="form-control">
<input id="auto-field-2" placeholder="Enter text" type="text" class="form-control gutter-top-40">
</div>
<div class="col-xs-2"></div>
</div>
SCSS
body {
background-color: #F3F5F6;
overflow-x: hidden;
}
@for $i from 1 through 10 {
$num: $i * 5;
.gutter-top-$num {
margin-top: $num + 'px';
}
}
JavaScript
$.fn.autocomplete = function({data, name, maxResults=Infinity, key}) {
if (!data || !data.length) throw('autocomplete needs data attribute');
let internalData = JSON.parse(JSON.stringify(data));
if (key)
internalData = internalData.map(d => d[key]);
let textfield = $(this);
// define names
let container = `autocomplete-${name}`;
let listgroup = `autocomplete-list-group-${name}`;
let idxDataAttr = `autocomplete=list-idx`;
let listContainer = $('<ul>').addClass(`list-group ${listgroup}`).css({position: 'absolute', width: textfield.outerWidth()});
// define some variables for maintaining state
let activeIndex = -1;
let listElements = [];
// define event handlers and functions
let changeSelectedItem = function(oldActiveIndex, force) {
let len = listElements.length;
if(activeIndex < 0 && !force) activeIndex = (len - 1);
else if(activeIndex >= len && !force) activeIndex = 0;
if(~oldActiveIndex)
listElements[oldActiveIndex].removeClass('active');
if(~activeIndex)
listElements[activeIndex].addClass('active');
if(force) {
$(`.${listgroup}`).html('');
listElements = [];
}
};
let itemClickHandler = function() {
let oldActiveIndex = activeIndex;
activeIndex = Number($(this).data(idxDataAttr));
textfield.val(listElements[activeIndex].text());
activeIndex = -1;
changeSelectedItem(oldActiveIndex, true);
};
// add some html
$(this).wrap(`<div class="${container}"><div>`)
$(`.${container}`).append(listContainer);
// add events
$(this).on('input', function() {
let val = $(this).val();
let filtered = val ? internalData.filter(str => str.toLowerCase().includes(val.toLowerCase())).slice(0, maxResults) : [];
listElements = filtered.map((f, idx) => $('<a>').addClass('list-group-item').text(f).data(idxDataAttr, idx).click(itemClickHandler));
$(`.${listgroup}`).html('');
listElements.forEach(l => $(`.${listgroup}`).append(l));
...