Events are action that can be detected by JavaScript. Every element on a web page has certain events which can trigger a javascript.
| Mouse Event | Description |
|---|---|
| Mouse Event | Description |
| onclick | Fires when an element receives a click activation. |
| ondblclick | Fires after two clicks occur on the same element in quick succession. |
| onmouseenter | Detects the pointer entering an element without bubbling from its children. |
| onmouseleave | Detects the pointer leaving an element without bubbling from its children. |
| onmouseover | Runs when the pointer enters an element or one of its descendants. |
| onmouseout | Runs when the pointer leaves an element or crosses one of its descendant boundaries. |
| onmouseup | Reports a mouse button being released over an element. |
| onmousedown | Reports a mouse button being pressed over an element. |
| onmousemove | Runs repeatedly as the pointer moves across an element. |
| oncontextmenu | Runs when the browser is about to open a context menu for an element. |
| Keyboard Event | Description |
| onkeyup | Fires when a pressed keyboard key is released. |
| onkeydown | Fires when a keyboard key is pressed and may repeat while it remains held. |
| onkeypress | A legacy character-key event; new code should normally use keydown. |
| Form Event | Description |
| onblur | Runs when an element loses focus; unlike focusout, blur does not bubble. |
| onchange | Runs after a form control commits a changed value, with timing based on the control type. |
| onfocus | Runs when an element receives focus; unlike focusin, focus does not bubble. |
| onfocusin | Bubbles when an element or one of its descendants receives focus. |
| onfocusout | Bubbles when focus leaves an element or one of its descendants. |
| oninput | Runs whenever the value of an input-capable control changes through user editing. |
| onsearch | |
| onselect | |
| onsubmit | |
| onreset | |
| oninvalid |
addEventListener is the recommended way to attach events. It allows multiple handlers on the same element and gives you full control over event propagation.
const btn = document.getElementById('myBtn');
// Add event listener
btn.addEventListener('click', function(event) {
console.log('Button clicked!');
console.log('Target:', event.target);
console.log('Type:', event.type);
});
// Arrow function handler
btn.addEventListener('mouseover', (e) => {
e.target.style.backgroundColor = '#3498db';
});
btn.addEventListener('mouseout', (e) => {
e.target.style.backgroundColor = '';
});
// Remove event listener (must use named function)
function handleClick(e) {
console.log('Clicked at:', e.clientX, e.clientY);
}
btn.addEventListener('click', handleClick);
btn.removeEventListener('click', handleClick);
// One-time event listener
btn.addEventListener('click', () => {
console.log('This fires only once');
}, { once: true });
Events bubble up from the target element to the root. Event delegation uses this to handle events on many child elements with a single listener on the parent.
// Event delegation - one listener handles all list items
const list = document.getElementById('myList');
list.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
console.log('Clicked item:', e.target.textContent);
e.target.classList.toggle('selected');
}
});
// Stop bubbling
document.addEventListener('click', () => console.log('Document clicked'));
btn.addEventListener('click', (e) => {
e.stopPropagation(); // prevents document handler from firing
console.log('Button clicked - stopped bubbling');
});
// Prevent default behavior
const link = document.querySelector('a');
link.addEventListener('click', (e) => {
e.preventDefault(); // stops navigation
console.log('Link click intercepted');
});
// Custom events
const customEvent = new CustomEvent('userLogin', {
detail: { username: 'Alice', timestamp: Date.now() }
});
document.dispatchEvent(customEvent);
document.addEventListener('userLogin', (e) => {
console.log('User logged in:', e.detail.username);
});
An event is dispatched to a target and travels through a defined propagation path. During the capture phase, listeners registered with `capture: true` run from outer ancestors toward the target. Target listeners then run, followed by bubbling listeners from the target toward outer ancestors when the event type bubbles. Not every event bubbles, so check the event contract instead of assuming delegation will work.
`event.target` identifies the object where dispatch began, subject to retargeting across component boundaries. `event.currentTarget` is the object whose listener is currently running. It changes as propagation moves and becomes unavailable after the callback returns, so copy only the specific values needed by delayed work rather than retaining the whole event object.
`stopPropagation()` prevents the event from reaching later objects on the path but does not stop other listeners already registered on the same object. `stopImmediatePropagation()` also prevents later listeners on that object. These methods can break analytics, accessibility helpers, and parent components, so use them only when the component owns the propagation contract.
`preventDefault()` requests cancellation of the browser default action, such as following a link or submitting a form. It does not stop propagation. Check `event.cancelable` when cancellation matters, and do not call it reflexively. Preserve native behavior unless the script supplies an accessible, reliable replacement.
`addEventListener` allows several listeners without replacing an existing handler property. The `capture` option selects the propagation phase, `once` removes the listener after its first invocation, and `passive` promises that the callback will not call `preventDefault()`. Passive listeners can improve scrolling behavior, but they are wrong when the interaction genuinely needs cancellation.
Pass an `AbortSignal` with the `signal` option when a group of listeners shares a lifetime. Calling `abort()` removes those listeners and can also cancel compatible fetch or stream work. This is useful for mounted views, dialogs, and temporary interactions because one lifecycle operation owns related resources.
`removeEventListener` needs the same event type, callback identity, and capture setting used for registration. A new inline arrow function is a different callback and cannot remove the old one. Keep a stable named callback or use an abort controller. Repeated mounting without cleanup causes duplicate actions, retained DOM, and increasingly expensive input handling.
Choose the narrowest useful listener target and lifetime. A permanent document-level listener for a temporary widget is easy to forget and difficult to attribute in a profiler. Initialize listeners after required elements exist, remove them before the owning state becomes invalid, and make teardown safe to call more than once.
Delegation attaches one bubbling listener to a stable ancestor and resolves the actionable descendant when an event arrives. It supports descendants added later and avoids one listener per row. Use `target.closest(selector)` to handle clicks on nested icons or spans, then verify that the matched element is contained by the delegating root so a match outside the owned subtree is not accepted.
The selected element should carry the action through a semantic element or a stable data attribute. Avoid branching on presentation classes or visible text. A button already provides focus, keyboard activation, and disabled behavior; a clickable `div` requires those semantics to be rebuilt and is usually the wrong starting point.
Shadow DOM retargets events to protect internal structure. `composedPath()` exposes the propagation path available to the caller, while the event type and its `composed` setting determine whether it can cross a shadow boundary. Components should expose a small event contract rather than requiring consumers to inspect private internal nodes.
Delegation is not automatically safer. Validate identifiers and current authorization when handling an action, especially after asynchronous work or DOM changes. The DOM is user-controlled input, not proof that an operation is permitted. Resolve the requested record through application state and enforce permission at the server boundary.
Pointer events provide one model for mouse, pen, and touch input. Use pointer capture only when a drag must continue after the pointer leaves the element, and release resources on `pointerup`, `pointercancel`, teardown, and lost capture. Do not require hover for essential actions because touch and keyboard users may never produce it.
Keyboard handlers should supplement semantic HTML rather than recreate it. Read `event.key` for intent and account for modifier keys, focus location, repeated keydown events, and text composition. Avoid global shortcuts while the user is typing in an editable control unless the shortcut is explicitly designed for that context.
The `input` event reports ongoing user edits, while `change` generally reports a committed value according to the control type. Listen for `submit` on the form so button activation, Enter, assistive technology, and `requestSubmit()` follow the same path. Validate at submission even if earlier input feedback is provided, and keep server validation authoritative.
High-frequency events such as pointer movement, scrolling, resizing, and input can overwhelm rendering or network work. Coalesce visual updates with `requestAnimationFrame`, debounce delayed searches where appropriate, and cancel obsolete requests. Throttling changes timing semantics, so test the final value, cancellation, and teardown rather than only the first callback.
`CustomEvent` carries application data in its `detail` property. Choose a stable, namespaced event name when unrelated components share the document, define the shape and ownership of detail, and avoid exposing secrets or mutable internal objects. A custom DOM event is useful for local integration, but a direct function call or state update is clearer when sender and receiver already share an application boundary.
`dispatchEvent()` runs matching listeners synchronously and returns after dispatch completes. Listener exceptions are reported according to browser behavior rather than becoming a normal return value from `dispatchEvent`. Programmatically dispatched events are not trusted user actions, and browser security checks may refuse operations that require user activation.
Test the public interaction through a real semantic control when possible. Assert the state change, default action decision, emitted custom-event detail, and cleanup rather than testing private handler calls alone. Include a nested child click for delegation, keyboard submission for forms, a canceled pointer sequence, and repeated initialization to expose lifecycle mistakes.
For propagation defects, log type, phase, target, current target, cancelable, default-prevented state, and a simplified composed path at a temporary diagnostic boundary. Remove noisy logging after diagnosis. Browser developer tools can reveal registered listeners and pause on events, while performance traces show handlers that block input or trigger repeated layout.
Explore 500+ free tutorials across 20+ languages and frameworks.