Basj Basj - 1 month ago 9
Javascript Question

Autocomplete usernames when TAB is pressed

I'm working on a "autocomplete usernames when TAB is pressed" feature.

I've been able to detect

@
and to search in a username list.

How to perform the autocompletion now, with a different matching username each time we press TAB ?



var userlist = ['bonjour', 'bleuet', 'bonobo', 'coucou'];

function getCaretPosition(ctrl) {
var start, end;
if (ctrl.setSelectionRange) {
start = ctrl.selectionStart;
end = ctrl.selectionEnd;
} else if (document.selection && document.selection.createRange) {
var range = document.selection.createRange();
start = 0 - range.duplicate().moveStart('character', -100000);
end = start + range.text.length;
}
return {
start: start,
end: end
}
}

$('#writing').keydown(function(e) {
if (e.keyCode == 9) {
var caret = getCaretPosition(this);
var word = /\S+$/.exec(this.value.slice(0, this.value.indexOf(' ',caret.end)));
word = word ? word[0] : null;
if (word.charAt(0) === '@')
alert(userlist.filter((x) => x.indexOf(word.slice(1)) === 0));
e.preventDefault();
return false;
}

});

#writing { width: 500px; }

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<textarea id="writing">Hello @b what's up? hello @you as well... Type @b and then "TAB" key it should autocomplete usernames
</textarea>




Answer

Here's a very basic example that works in modern browsers (Chrome, Firefox, Edge):

const users = ['asdasd', 'fgsfds', 'Foobar']
const input = window.input
const patt = /\S+$/

input.addEventListener('keydown', e => {
  if (e.key !== 'Tab') {
    return
  }
  e.preventDefault()
  const start = input.selectionStart
  const seg = input.value.slice(0, start)
  const match = (seg.match(patt) || [])[0]
  if (!match) {
    return
  }
  const idx = users.findIndex(x => x.startsWith(match))
  if (idx < 0) {
    return
  }
  const replace = users[users[idx] === match ? (idx + 1) % users.length : idx]
  const newSeg = seg.replace(patt, replace)
  input.value = newSeg + input.value.slice(start)
  input.setSelectionRange(newSeg.length, newSeg.length)
})
<input type="text" id="input" size="50" value="bla asd bla fgs">

It cycles through the names and works at any point in the line.