Throughout my own findings, I figured this works really well:
function limit_input(n) { // n = number of bytes
return function(e) {
const is_clipboard = e instanceof ClipboardEvent;
if(is_clipboard && e.type != "paste") {
return;
}
let new_val = e.target.value;
if(is_clipboard) {
new_val += e.clipboardData.getData("text");
} else {
new_val += e.key;
}
if(new TextEncoder().encode(new_val).byteLength -
e.target.selectionEnd + e.target.selectionStart > n) {
if(e.target.value == "" && is_clipboard) {
const old = e.target.placeholder;
e.target.placeholder = "Text too long to paste!";
setTimeout(function() {
e.target.placeholder = old;
}, 1000);
}
e.preventDefault();
}
};
}
let el = document.getElementById("your_input");
el.onkeypress = el.onpaste = limit_input(4);
I started out with dandavis' answer and kept on improving it to adapt to all situations. I still don't think this is perfect, and it's still using the deprecated onkeypress
handler, but nothing else worked better than this.
You can delete the part of the code that changes placeholder
to say the text is too long to paste (delete the whole if
, keep only e.preventDefault()
in). It's just something I added myself to notify the user why the input is still empty after they try pasting something in. That way they won't blame me for writing faulty code and I won't have to answer a horde of complaints.