Explain Codes LogoExplain Codes Logo

Best way to track onchange as-you-type in input type="text"?

javascript
event-handling
debouncing
input-event
Alex KataevbyAlex Kataev·Sep 25, 2024
TLDR

For real-time text changes, use the input event on <input> elements.

document.querySelector('#input').addEventListener('input', e => console.log(e.target.value));

This logs every change—typing, deleting, or pastinginstantaneously.

oninput vs onchange: knowing the difference

The input event is distinct from the onchange event. onchange waits until focus is lost, whereas input fires immedately when there's a change. This provides instant feedback, ideal for UIs needing to validate, format, or filter text on-the-go.

For legacy Internet Explorer, the oninput and onpropertychange events were combined for similar functionality. Yet, it's best to stick with the WHATWG HTML standard for cross-browser compliance.

Handling quirky cases and implementing wide-ranging solutions

Ensuring backwards compatibility

When broad compatibility is the goal, employ feature detection for graceful degradation. Users of older browsers might not get the finest experience but they'll have functionality.

let inputElement = document.querySelector('input'); let inputHandler = function(event) { console.log(event.target.value); // 👀 spying on user input }; if (inputElement.oninput === undefined) { // IE? Is that you? inputElement.onpropertychange = inputHandler; } else { inputElement.oninput = inputHandler; }

This code checks for the input event support in the browser. If it doesn't, it falls back to onpropertychange.

Monitoring paste events effectively

Users might paste text instead of typing it, so you need to handle paste actions separately.

inputElement.addEventListener('paste', (e) => { // Wait is the hardest part. Thanks Tom Petty! setTimeout(() => console.log(inputElement.value), 0); });

Here, the onpaste event is combined with a minimal setTimeout allowing for the pasted content to render in the field.

Boosting performance with debouncing

For heavy and sequential input, performance can dwindle. Here, debouncing comes into play. By using a wrapper function to delay execution until the user stops typing, you can optimize browser performance.

let debounce = (func, delay) => { let inDebounce; return function() { clearTimeout(inDebounce); // I'll do it... later. inDebounce = setTimeout(() => func.apply(this, arguments), delay); }; }; inputElement.addEventListener('input', debounce((e) => { console.log(e.target.value); }, 250));

This design ensures that the event handler only fires after the user takes a break, thereby freeing up resources and improving performance.

Addressing Safari's textarea quirk

For <textarea> elements in Safari, the input event can be problematic. Instead, go for the textInput event:

let textAreaElement = document.querySelector('textarea'); textAreaElement.addEventListener('textInput', e => console.log(e.data));

This focuses on pure text input, providing consistent and precise change detection.