JavaScript Events
Events are "signals" sent by the user or the browser—when a user clicks a button, presses a key, submits a form, or the browser finishes loading a page... these are all events. Your code "listens" for these events and responds at the right moment. It's like a doorbell ringing and you opening the door—the doorbell is the event, opening the door is the response.
Event Binding Methods
JavaScript has three ways to bind event handlers to elements:
1. HTML Attributes (Not Recommended)
<button onclick="alert('Don't use this method')">Click me</button>
HTML and JS are mixed together, making it hard to maintain. Not recommended.
2. DOM Properties
<button id="myBtn">Click me</button>
<script>
const btn = document.getElementById('myBtn');
btn.onclick = function() {
console.log('Clicked');
};
</script>
HTML and JS are separated, but each event can only have one handler—later assignments overwrite earlier ones.
3. addEventListener (Recommended)
<button id="btn1">Button 1</button>
<button id="btn2">Button 2</script>
<script>
const btn1 = document.getElementById('btn1');
const btn2 = document.getElementById('btn2');
btn1.addEventListener('click', function() {
console.log('Handler 1');
});
btn1.addEventListener('click', function() {
console.log('Handler 2');
});
</script>
The same event can have multiple handlers, and you can use removeEventListener to remove them. This is the most flexible approach.
addEventListener. It doesn't overwrite existing listeners and lets you control the bubbling/capturing phase. It's the only choice for modern development.
addEventListener and removeEventListener
<script>
element.addEventListener('eventName', handler, options);
element.removeEventListener('eventName', handler);
</script>
removeEventListener requires the same function reference as addEventListener. Anonymous functions cannot be removed! If you need to remove a handler, define the function separately.
<button id="btn">Test</button>
<script>
const btn = document.getElementById('btn');
function handler() { console.log('Triggers only once'); }
btn.addEventListener('click', handler);
btn.removeEventListener('click', handler); // Successfully removed
btn.addEventListener('click', function() { console.log('Anonymous'); });
btn.removeEventListener('click', function() { console.log('Anonymous'); }); // Removal fails!
</script>
Common Event Types
| Category | Event | Description |
|---|---|---|
| Mouse | click, dblclick |
Single click, double click |
| Mouse | mouseover, mouseout |
Mouse enters/leaves |
| Keyboard | keydown, keyup |
Key pressed/released |
| Form | submit |
Form submitted |
| Form | change |
Value changed after losing focus |
| Form | input |
Value changes in real-time (each character typed) |
| Page | load |
Page and all resources fully loaded |
| Page | DOMContentLoaded |
HTML parsing complete (doesn't wait for images) |
change vs input: input fires as you type, change fires after you finish typing and lose focus. Use input for real-time search, change for form validation.
Event Object
Event handlers receive an event object parameter, usually called event or abbreviated as e.
| Property/Method | Description |
|---|---|
e.target |
The element that triggered the event (the one actually clicked) |
e.type |
Event type, like 'click' |
e.preventDefault() |
Prevent default behavior (e.g., prevent link navigation, form submission) |
e.stopPropagation() |
Stop event bubbling |
Example: Button Click Counter
<button id="counter">Clicked 0 times</button>
<script>
const btn = document.getElementById('counter');
let count = 0;
btn.addEventListener('click', function(e) {
count++;
btn.textContent = 'Clicked ' + count + ' times';
console.log('Event type:', e.type);
console.log('Target element:', e.target.tagName);
});
</script>
Example: Keyboard Detection
<p>Press any key to see information</p>
<div id="info" style="padding: 10px; border: 1px solid #ccc; min-height: 30px;"></div>
<script>
const info = document.getElementById('info');
document.addEventListener('keydown', function(e) {
info.innerHTML = 'Key: <b>' + e.key + '</b> | KeyCode: ' + e.keyCode + ' | Ctrl: ' + e.ctrlKey + ' | Shift: ' + e.shiftKey;
});
</script>
Example: Form Interception
<form id="myForm">
<input type="text" id="name" placeholder="Enter name" required>
<button type="submit">Submit</button>
</form>
<p id="result"></p>
<script>
const form = document.getElementById('myForm');
const result = document.getElementById('result');
form.addEventListener('submit', function(e) {
e.preventDefault();
const name = document.getElementById('name').value;
result.textContent = 'Hello, ' + name + '! Form was intercepted, not actually submitted.';
});
</script>
Example: Event Delegation
<ul id="menu">
<li>Home</li>
<li>About</li>
<li>Contact</li>
<li>Help</li>
</ul>
<p id="selected"></p>
<script>
const menu = document.getElementById('menu');
const selected = document.getElementById('selected');
menu.addEventListener('click', function(e) {
if (e.target.tagName === 'LI') {
selected.textContent = 'You selected: ' + e.target.textContent;
}
});
</script>
e.target to determine which child was actually clicked. You don't need to bind events to each li individually, and new child elements automatically work. This is the power of the bubbling mechanism.
Event Bubbling and Capturing
DOM event propagation has three phases:
- Capturing phase: Event travels from
windowdown to the target element - Target phase: Event reaches the actual triggering element
- Bubbling phase: Event bubbles up from the target element back to
window
By default, addEventListener triggers during the bubbling phase. Set the third parameter to true to trigger during the capturing phase.
window
↓ capturing
document
↓
body
↓
target element ← target phase
↑ bubbling
body
↑
document
↑
window
e.stopPropagation() can stop event propagation, but don't overuse it—it might break event delegation.
📖 Summary
- Among the three binding methods,
addEventListeneris the only recommended one—supports multiple listeners, removable, and controllable propagation phase. removeEventListenerrequires the same function reference; anonymous functions cannot be removed.- Core event object
eproperties:target(who triggered it),type(what event),preventDefault()(prevent default behavior),stopPropagation()(stop propagation). changevsinput: the former fires on blur, the latter fires in real-time.- Event bubbling makes "event delegation" possible—listen on the parent, handle children with
e.target. DOMContentLoadedfires earlier thanload, suitable for DOM initialization.
❓ FAQ
e.target and e.currentTarget?e.target is the element that actually triggered the event (the one you clicked), e.currentTarget is the element currently executing the handler (the one with the listener). In event delegation they're different; in direct binding they're the same.<form>'s submit event default behavior is to submit and refresh the page. Call e.preventDefault() to prevent it, then handle the data with JS.onclick and addEventListener('click') be used together?onclick assignment overwrites functions previously bound via onclick, while addEventListener doesn't. Mixing them can be confusing—stick with addEventListener for peace of mind.📝 Exercises
- Basic: Create a button and use
addEventListenerto bind aclickevent. Each click increases the button's width by 10px. - Intermediate: Implement a keyboard instrument—listen for
keydownevents. When A/S/D/F/G is pressed, display different note names (Do/Re/Mi/Fa/Sol) on the page. - Challenge: Use event delegation to implement a dynamic menu—a
ullist with an "Add Item" button. Newly addedliitems should also highlight when clicked (no need to bind events to newlielements individually). - Bonus: Implement a real-time search with
inputevent—as you type in the input field, the list below only shows items containing the typed text.



