Three events for typing
When the user types in a field, several events fire for each key:
keydown: the key went down. The character has not been inserted yet.input: the field's value changed.keyup: the key came back up.
<input id="name" placeholder="Type here">
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#name")
.on("keydown", e => console.log("keydown", e.key, "| value before:", JSON.stringify(e.target.value)))
.on("input", e => console.log("input | value now:", JSON.stringify(e.target.value)))
.on("keyup", e => console.log("keyup ", e.key));
</script>keydown H | value before: ""
input | value now: "H"
keyup H
keydown i | value before: "H"
input | value now: "Hi"
keyup iNotice that during keydown the value does not yet include the new letter.
Use input for live updates
If you want to react to what is in the field, listen for input. It fires for every change, however it happens: typing, pasting with the mouse, autofill, voice dictation, or dragging text in. Key events miss most of those.
A character counter is a typical example:
<textarea id="bio" maxlength="40" rows="2" cols="40"></textarea>
<p><span id="left">40</span> characters left</p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#bio").on("input", function () {
const left = 40 - $(this).val().length;
$("#left").text(left).css("color", left < 10 ? "crimson" : "");
});
$("#bio").on("input", function () {
if ($(this).val().length === 12) console.log("left:", $("#left").text());
});
</script>left: 28The older keypress event is deprecated in browsers; do not use it in new code.
Reacting to particular keys
Use keydown and check event.key when the key matters rather than the value: Enter to submit, Escape to close, arrows to move through a list.
<input id="q" placeholder="Use the arrow keys">
<ul id="suggestions"><li>Lamp</li><li>Desk</li><li>Chair</li></ul>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
let active = -1;
const $items = $("#suggestions li");
$("#q").on("keydown", function (event) {
if (event.key === "ArrowDown") active = Math.min(active + 1, $items.length - 1);
else if (event.key === "ArrowUp") active = Math.max(active - 1, 0);
else if (event.key === "Enter") { console.log("chosen:", $items.eq(active).text()); return; }
else return;
event.preventDefault(); // stop the cursor jumping in the input
$items.css("background", "").eq(active).css("background", "#fde68a");
console.log("highlighted:", $items.eq(active).text());
});
</script>highlighted: Lamp
highlighted: Desk
highlighted: Lamp
chosen: LampDown, down, up and Enter moved the highlight to Lamp, then Desk, back to Lamp, and chose it. Calling preventDefault() for the arrow keys stops their normal effect (moving the text cursor).
Blocking characters
Because keydown happens before the character is inserted, cancelling it stops the typing. For example, a field that accepts only digits:
<input id="pin" inputmode="numeric" placeholder="Digits only">
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#pin").on("keydown", function (event) {
const allowed = /^[0-9]$/.test(event.key) || event.key.length > 1; // digits, or keys like Backspace and Tab
if (!allowed) event.preventDefault();
});
$("#pin").on("input", function () {
$(this).val($(this).val().replace(/\D/g, "")); // also clean pasted text
console.log("value:", $(this).val());
});
</script>value: 1
value: 12
value: 123
value: 1234Keys with long names, like Backspace, Tab and the arrows, are always allowed, so the field stays usable. The letters a and b were blocked, so the value grew 1, 12, 123, 1234. The input handler is a second line of defence for pasted text, which never fires keydown. Always validate again on the server: users can bypass any browser check.
Debouncing: wait for a pause
Running a search request on every key wastes requests. Debouncing waits until the user stops typing for a moment:
<input id="search" placeholder="Search products">
<p id="status"></p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
let timer;
$("#search").on("input", function () {
clearTimeout(timer);
const term = $(this).val().trim();
timer = setTimeout(function () {
$("#status").text("Searching for " + term);
console.log("search sent for:", term);
}, 300);
});
</script>search sent for: lampTyping four letters scheduled the search four times, but each new letter cancelled the previous timer, so only one search ran, for the whole word.
Keyboard shortcuts
To add a shortcut for the whole page, listen on document, and ignore keys typed into fields:
$(document).on("keydown", function (event) {
if ($(event.target).is("input, textarea, select, [contenteditable]")) return;
if (event.key === "/") {
event.preventDefault();
$("#search").trigger("focus");
}
});Avoid shortcuts that clash with the browser or screen readers, and never make a shortcut the only way to do something.
Common mistakes
- Using
keyupfor live updates, which misses pasting and autofill. Useinput. - Using
keypress, which is deprecated. - Checking
event.keyCodenumbers instead ofevent.key. - Sending a request on every key instead of debouncing.
- Page-wide shortcuts that fire while the user types in a field.
Exercise
Make an input that shows, below it, the text typed so far in capital letters, updating live. When the user presses Escape, clear the input and the preview.
Show one possible answer
<input id="t">
<p id="upper"></p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
$("#t")
.on("input", function () { $("#upper").text($(this).val().toUpperCase()); })
.on("keydown", function (event) {
if (event.key === "Escape") { $(this).val(""); $("#upper").text(""); console.log("cleared"); }
});
</script>clearedKey points
- input fires whenever a field's value changes, including paste, autofill and voice typing; use it for live updates.
- keydown fires when a key is pressed and can be cancelled; keyup fires when it is released.
- event.key holds the key's value, such as "Enter", "Escape", "ArrowDown" or "a".
- For work that is expensive, such as a search request, wait until the user pauses typing (debouncing).