Validated Input Field
An "input" that highlights invalid parts
by TigerAsks
HTML
<div class="validated-input" contenteditable="true">HAKUNAMATATA</div>
CSS
.validated-input{
color: blue;
white-space: nowrap;
overflow-x: auto;
width: 20em;
border: solid 1px black;
}
.validated-input .error{
color: red;
}
JavaScript
const input = document.querySelector(".validated-input")
const specialKeys = ['Enter', 'Backspace', 'Delete', 'ArrowLeft', 'ArrowUp', 'ArrowRight', 'ArrowDown', 'Shift', 'Alt', 'Control', 'Esc']
const invalidKeys = /[^AC-IK-NP-TVWY]/
//disallow multi-line input
input.addEventListener('keydown', e => {
if ('Enter' === e.key) {
e.stopPropagation()
e.preventDefault()
input.blur()
}
})
//highlight invalid characters on keyup
input.addEventListener('keyup', e => {
if (!specialKeys.includes(e.key)) {
e.stopPropagation()
validateInput()
}
})
function validateInput() {
const cursorPosition = getCursorPositionWithin(input)
console.log(cursorPosition)
const value = input.innerText.toUpperCase()
input.innerHTML = validate(value)
setCursorPositionWithin(input, cursorPosition)
}
function validate(value) {
console.log(value)
if (!value) return ''
value = [...value]
let formatted = ''
for (start = 0; start < value.length;) {
if (!invalidKeys.test(value[start])) {
formatted += value[start++]
continue
}
let stop = start
while (++stop < value.length && invalidKeys.test(value[stop])) {}
let invalids = value.slice(start, stop).join('')
formatted += `<span class="error">${invalids}</span>`
start = stop
}
console.log(formatted)
return formatted
}
function getCursorPositionWithin(element) {
//credit: https://stackoverflow.com/a/4812022/3322533
const doc = element.ownerDocument || element.document;
const win = doc.defaultView || doc.parentWindow;
let caretOffset = 0;
let sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
let range = win.getSelection().getRangeAt(0);
let preCaretRange = range.cloneRange();
console.log(range)
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
}...