3.4 אירועים הרצאה
מה זה אירוע - event¶
אירוע הוא כל דבר שקורה בדף - המשתמש לחץ על כפתור, הזיז את העכבר, הקליד על המקלדת, שינה את גודל החלון, או שהדף סיים להיטען. JavaScript מאפשר לנו "להקשיב" לאירועים האלה ולהגיב אליהם.
- בלי אירועים, אתרים היו סטטיים ולא אינטראקטיביים
- אירועים הם הבסיס לכל אינטראקציה עם המשתמש
- כמעט כל דבר שקורה בדפדפן יוצר אירוע
addEventListener - הדרך הנכונה¶
המתודה addEventListener מאפשרת לנו להוסיף listener (מאזין) שירוץ כשאירוע מסוים קורה:
const btn = document.getElementById("my-btn");
// add a click event listener
btn.addEventListener("click", function() {
console.log("Button was clicked!");
});
התחביר: element.addEventListener(eventType, handlerFunction)
- הפרמטר הראשון הוא סוג האירוע כמחרוזת
- הפרמטר השני הוא הפונקציה שתרוץ כשהאירוע קורה (נקראת handler או callback)
- אפשר להוסיף כמה listeners לאותו אירוע על אותו אלמנט
const btn = document.getElementById("my-btn");
// multiple listeners on the same event
btn.addEventListener("click", function() {
console.log("First handler");
});
btn.addEventListener("click", function() {
console.log("Second handler");
});
// both will run when the button is clicked
removeEventListener - הסרת מאזין¶
כדי להסיר listener, צריך לשמור reference לפונקציה:
const btn = document.getElementById("my-btn");
// WRONG - can't remove anonymous functions
btn.addEventListener("click", function() {
console.log("Can't remove this!");
});
// RIGHT - save the function reference
function handleClick() {
console.log("This can be removed!");
}
btn.addEventListener("click", handleClick);
// later, remove it
btn.removeEventListener("click", handleClick);
- חייבים להעביר את אותה פונקציה בדיוק (אותו reference)
- פונקציות אנונימיות לא ניתנות להסרה כי אין להן reference
סוגי אירועים¶
אירועי עכבר - mouse events¶
<div id="box" style="width: 200px; height: 200px; background: lightblue; padding: 20px;">
Hover and click me!
</div>
const box = document.getElementById("box");
// click - left mouse button click (down + up)
box.addEventListener("click", function() {
console.log("clicked!");
});
// dblclick - double click
box.addEventListener("dblclick", function() {
console.log("double clicked!");
});
// mousedown - mouse button pressed down
box.addEventListener("mousedown", function() {
console.log("mouse button down");
});
// mouseup - mouse button released
box.addEventListener("mouseup", function() {
console.log("mouse button up");
});
// mouseenter - mouse enters the element (doesn't bubble)
box.addEventListener("mouseenter", function() {
box.style.backgroundColor = "lightgreen";
});
// mouseleave - mouse leaves the element (doesn't bubble)
box.addEventListener("mouseleave", function() {
box.style.backgroundColor = "lightblue";
});
// mousemove - mouse moves inside the element
box.addEventListener("mousemove", function(event) {
console.log("Mouse at:", event.clientX, event.clientY);
});
// contextmenu - right click (can prevent default context menu)
box.addEventListener("contextmenu", function(event) {
event.preventDefault(); // prevent the default context menu
console.log("Right clicked! Custom menu could appear here.");
});
ההבדל בין mouseenter/mouseleave ל-mouseover/mouseout:
// mouseover/mouseout - triggers also when entering/leaving child elements
// mouseenter/mouseleave - only triggers for the element itself, not children
// usually mouseenter/mouseleave is what you want
אירועי מקלדת - keyboard events¶
const input = document.getElementById("text-input");
// keydown - fires when a key is pressed
input.addEventListener("keydown", function(event) {
console.log("Key down:", event.key);
});
// keyup - fires when a key is released
input.addEventListener("keyup", function(event) {
console.log("Key up:", event.key);
});
// keypress is DEPRECATED - don't use it
// use keydown instead
keydownקורה ברגע שהמקש נלחץkeyupקורה כשהמקש משוחררkeypressהוא deprecated - לא להשתמש בו
אירועי פוקוס - focus events¶
const input = document.getElementById("text-input");
// focus - element receives focus
input.addEventListener("focus", function() {
console.log("Input focused!");
input.style.borderColor = "blue";
});
// blur - element loses focus
input.addEventListener("blur", function() {
console.log("Input lost focus!");
input.style.borderColor = "";
});
// focusin / focusout - like focus/blur but they bubble
// useful for event delegation on forms
אירועי טופס - form events¶
<form id="my-form">
<input type="text" id="name" name="name">
<select id="color">
<option value="red">Red</option>
<option value="blue">Blue</option>
</select>
<button type="submit">Submit</button>
</form>
const form = document.getElementById("my-form");
const nameInput = document.getElementById("name");
const colorSelect = document.getElementById("color");
// submit - form is submitted
form.addEventListener("submit", function(event) {
event.preventDefault(); // prevent page reload
console.log("Form submitted!");
console.log("Name:", nameInput.value);
console.log("Color:", colorSelect.value);
});
// input - fires on every change (typing, pasting, etc.)
nameInput.addEventListener("input", function() {
console.log("Current value:", nameInput.value);
});
// change - fires when value changes AND element loses focus
// for select/checkbox/radio, fires immediately on change
colorSelect.addEventListener("change", function() {
console.log("Color changed to:", colorSelect.value);
});
// reset - form is reset
form.addEventListener("reset", function() {
console.log("Form was reset!");
});
input- נורה על כל שינוי בזמן אמת (מתאים לחיפוש חי, ולידציה תוך כדי הקלדה)change- נורה כשהערך שונה וה-input מאבד פוקוס (או מיד ב-select, checkbox, radio)
אירועי חלון - window events¶
// load - everything is loaded (images, CSS, etc.)
window.addEventListener("load", function() {
console.log("Page fully loaded!");
});
// DOMContentLoaded - DOM is ready
document.addEventListener("DOMContentLoaded", function() {
console.log("DOM is ready!");
});
// resize - window is resized
window.addEventListener("resize", function() {
console.log("Window size:", window.innerWidth, "x", window.innerHeight);
});
// scroll - page is scrolled
window.addEventListener("scroll", function() {
console.log("Scroll position:", window.scrollY);
});
// beforeunload - user is about to leave the page
window.addEventListener("beforeunload", function(event) {
// show a confirmation dialog
event.preventDefault();
// some browsers require returnValue
event.returnValue = "";
});
אירועי לוח - clipboard events¶
const input = document.getElementById("text-input");
// copy
input.addEventListener("copy", function(event) {
console.log("Text copied!");
});
// paste
input.addEventListener("paste", function(event) {
console.log("Text pasted!");
// access clipboard data
const pastedText = event.clipboardData.getData("text");
console.log("Pasted:", pastedText);
});
// cut
input.addEventListener("cut", function(event) {
console.log("Text cut!");
});
אירועי גרירה - drag events¶
<div id="draggable" draggable="true" style="width: 100px; height: 100px; background: orange;">
Drag me!
</div>
<div id="drop-zone" style="width: 300px; height: 300px; border: 2px dashed gray;">
Drop here!
</div>
const draggable = document.getElementById("draggable");
const dropZone = document.getElementById("drop-zone");
// on the draggable element
draggable.addEventListener("dragstart", function(event) {
event.dataTransfer.setData("text/plain", "dragged!");
console.log("Drag started!");
});
draggable.addEventListener("dragend", function() {
console.log("Drag ended!");
});
// on the drop zone
dropZone.addEventListener("dragover", function(event) {
event.preventDefault(); // required to allow drop
dropZone.style.backgroundColor = "#e3f2fd";
});
dropZone.addEventListener("dragleave", function() {
dropZone.style.backgroundColor = "";
});
dropZone.addEventListener("drop", function(event) {
event.preventDefault();
const data = event.dataTransfer.getData("text/plain");
console.log("Dropped:", data);
dropZone.style.backgroundColor = "";
dropZone.appendChild(draggable);
});
אירועי מגע - touch events¶
const box = document.getElementById("box");
box.addEventListener("touchstart", function(event) {
console.log("Touch started!");
console.log("Touch position:", event.touches[0].clientX, event.touches[0].clientY);
});
box.addEventListener("touchmove", function(event) {
console.log("Touch moving...");
});
box.addEventListener("touchend", function() {
console.log("Touch ended!");
});
אובייקט האירוע - event object¶
כל handler מקבל אובייקט אירוע שמכיל מידע על מה שקרה:
const btn = document.getElementById("my-btn");
btn.addEventListener("click", function(event) {
// the event object contains information about the event
console.log("Event type:", event.type); // "click"
console.log("Target element:", event.target); // the element that was clicked
console.log("Current target:", event.currentTarget); // the element the listener is on
});
event.target מול event.currentTarget¶
<div id="parent" style="padding: 20px; background: lightblue;">
<button id="child">Click me</button>
</div>
const parent = document.getElementById("parent");
parent.addEventListener("click", function(event) {
console.log("target:", event.target); // the element that was actually clicked
console.log("currentTarget:", event.currentTarget); // always the element with the listener
});
// if you click the button:
// target = <button> (what you clicked)
// currentTarget = <div> (where the listener is)
// if you click the div (not the button):
// target = <div> (what you clicked)
// currentTarget = <div> (where the listener is)
event.target- האלמנט שהמשתמש באמת לחץ עליוevent.currentTarget- האלמנט שה-listener רשום עליו- ההבדל חשוב כשעובדים עם event delegation (נלמד בשיעור הבא)
preventDefault - ביטול התנהגות ברירת מחדל¶
// prevent form submission (page reload)
const form = document.getElementById("my-form");
form.addEventListener("submit", function(event) {
event.preventDefault();
console.log("Form handled by JavaScript instead!");
});
// prevent link navigation
const link = document.querySelector("a");
link.addEventListener("click", function(event) {
event.preventDefault();
console.log("Link click prevented!");
});
// prevent right-click context menu
document.addEventListener("contextmenu", function(event) {
event.preventDefault();
});
stopPropagation - עצירת בעבוע¶
כשאירוע קורה על אלמנט, הוא "מבעבע" (bubbles) למעלה לכל ההורים. אפשר לעצור את זה:
<div id="outer" style="padding: 30px; background: lightcoral;">
<div id="inner" style="padding: 30px; background: lightblue;">
<button id="btn">Click</button>
</div>
</div>
document.getElementById("outer").addEventListener("click", function() {
console.log("Outer clicked");
});
document.getElementById("inner").addEventListener("click", function() {
console.log("Inner clicked");
});
document.getElementById("btn").addEventListener("click", function(event) {
console.log("Button clicked");
event.stopPropagation(); // stops the event from bubbling up
});
// without stopPropagation: clicking the button prints all three
// with stopPropagation: clicking the button prints only "Button clicked"
נלמד על בעבוע לעומק בשיעור הבא.
מאפייני אירועי עכבר¶
document.addEventListener("click", function(event) {
// position relative to the viewport (visible area)
console.log("clientX:", event.clientX);
console.log("clientY:", event.clientY);
// position relative to the entire page (including scroll)
console.log("pageX:", event.pageX);
console.log("pageY:", event.pageY);
// position relative to the screen
console.log("screenX:", event.screenX);
console.log("screenY:", event.screenY);
// which button was pressed (0=left, 1=middle, 2=right)
console.log("button:", event.button);
});
מאפייני אירועי מקלדת¶
document.addEventListener("keydown", function(event) {
// the key that was pressed (readable name)
console.log("key:", event.key); // "a", "Enter", "ArrowUp", "Shift"
// the physical key code
console.log("code:", event.code); // "KeyA", "Enter", "ArrowUp", "ShiftLeft"
// modifier keys
console.log("Alt:", event.altKey);
console.log("Ctrl:", event.ctrlKey);
console.log("Shift:", event.shiftKey);
console.log("Meta (Cmd/Win):", event.metaKey);
});
event.key- שם המקש (מושפע ממצב Shift ושפה)event.code- קוד המקש הפיזי (תמיד אותו דבר)
// example: detect keyboard shortcuts
document.addEventListener("keydown", function(event) {
// Ctrl+S (or Cmd+S on Mac)
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
event.preventDefault(); // prevent browser save dialog
console.log("Save shortcut detected!");
}
// Escape key
if (event.key === "Escape") {
console.log("Escape pressed!");
}
// Arrow keys
if (event.key === "ArrowUp") {
console.log("Up arrow pressed!");
}
});
למה לא להשתמש ב-inline handlers¶
יש דרך ישנה לטפל באירועים ישירות ב-HTML:
<!-- DON'T do this -->
<button onclick="alert('clicked!')">Click me</button>
<button onclick="handleClick()">Click me</button>
למה זה רע:
- ערבוב של HTML ו-JavaScript
- לא ניתן להוסיף כמה handlers לאותו אירוע
- קשה לתחזוקה בפרויקטים גדולים
- בעיות אבטחה (Content Security Policy)
- תמיד נשתמש ב-addEventListener
אפשרויות ל-addEventListener¶
// once - the handler runs only once, then automatically removes itself
const btn = document.getElementById("my-btn");
btn.addEventListener("click", function() {
console.log("This will only run once!");
}, { once: true });
// passive - tells the browser this handler won't call preventDefault
// improves scroll performance
window.addEventListener("scroll", function() {
console.log("Scrolling...");
}, { passive: true });
once: true- ה-handler ירוץ פעם אחת בלבד ואחר כך יוסר אוטומטיתpassive: true- אומר לדפדפן שה-handler לא יקרא ל-preventDefault, מה שמאפשר ביצועים טובים יותר (חשוב במיוחד ל-scroll ו-touch)
אירועים מותאמים - CustomEvent¶
אפשר ליצור אירועים משלנו:
// create a custom event
const myEvent = new CustomEvent("userLoggedIn", {
detail: { username: "john", role: "admin" }
});
// listen for the custom event
document.addEventListener("userLoggedIn", function(event) {
console.log("User logged in:", event.detail.username);
console.log("Role:", event.detail.role);
});
// dispatch (trigger) the event
document.dispatchEvent(myEvent);
CustomEventמאפשר ליצור אירועים עם מידע מותאם אישיתdetailמכיל את המידע שרוצים להעביר עם האירועdispatchEventשולח את האירוע- שימושי ליצירת תקשורת בין חלקים שונים של האפליקציה
דוגמאות מעשיות¶
דוגמה 1 - מעקב אחרי העכבר¶
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Mouse Tracker</title>
<style>
body { margin: 0; height: 100vh; font-family: Arial, sans-serif; }
#coords { position: fixed; top: 10px; right: 10px; background: rgba(0,0,0,0.7); color: white; padding: 10px; border-radius: 5px; }
#dot { width: 20px; height: 20px; background: red; border-radius: 50%; position: absolute; pointer-events: none; transform: translate(-50%, -50%); }
</style>
</head>
<body>
<div id="coords">X: 0, Y: 0</div>
<div id="dot"></div>
<script>
const coords = document.getElementById("coords");
const dot = document.getElementById("dot");
document.addEventListener("mousemove", function(event) {
coords.textContent = "X: " + event.clientX + ", Y: " + event.clientY;
dot.style.left = event.clientX + "px";
dot.style.top = event.clientY + "px";
});
</script>
</body>
</html>
דוגמה 2 - ולידציה בזמן אמת¶
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<title>Live Validation</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
.form-group { margin: 15px 0; }
label { display: block; margin-bottom: 5px; }
input { padding: 8px; width: 300px; border: 2px solid #ddd; border-radius: 4px; }
input.valid { border-color: green; }
input.invalid { border-color: red; }
.error-message { color: red; font-size: 14px; margin-top: 5px; }
.success-message { color: green; font-size: 14px; margin-top: 5px; }
</style>
</head>
<body>
<h1>Registration Form</h1>
<form id="registration-form">
<div class="form-group">
<label for="username">Username (at least 3 characters):</label>
<input type="text" id="username">
<div id="username-feedback"></div>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email">
<div id="email-feedback"></div>
</div>
<div class="form-group">
<label for="password">Password (at least 6 characters):</label>
<input type="password" id="password">
<div id="password-feedback"></div>
</div>
<button type="submit">Register</button>
</form>
<script>
const form = document.getElementById("registration-form");
const usernameInput = document.getElementById("username");
const emailInput = document.getElementById("email");
const passwordInput = document.getElementById("password");
function showFeedback(feedbackEl, inputEl, isValid, message) {
feedbackEl.textContent = message;
if (isValid) {
feedbackEl.className = "success-message";
inputEl.className = "valid";
} else {
feedbackEl.className = "error-message";
inputEl.className = "invalid";
}
}
// validate username on input (real-time)
usernameInput.addEventListener("input", function() {
const feedback = document.getElementById("username-feedback");
const value = usernameInput.value.trim();
if (value.length === 0) {
feedback.textContent = "";
usernameInput.className = "";
} else if (value.length < 3) {
showFeedback(feedback, usernameInput, false,
"Username must be at least 3 characters");
} else {
showFeedback(feedback, usernameInput, true, "Username looks good!");
}
});
// validate email on blur (when leaving the field)
emailInput.addEventListener("blur", function() {
const feedback = document.getElementById("email-feedback");
const value = emailInput.value.trim();
if (value === "") return;
if (value.includes("@") && value.includes(".")) {
showFeedback(feedback, emailInput, true, "Email looks valid!");
} else {
showFeedback(feedback, emailInput, false, "Please enter a valid email");
}
});
// validate password on input
passwordInput.addEventListener("input", function() {
const feedback = document.getElementById("password-feedback");
const value = passwordInput.value;
if (value.length === 0) {
feedback.textContent = "";
passwordInput.className = "";
} else if (value.length < 6) {
showFeedback(feedback, passwordInput, false,
"Password must be at least 6 characters (" + value.length + "/6)");
} else {
showFeedback(feedback, passwordInput, true, "Password is strong enough!");
}
});
// handle form submission
form.addEventListener("submit", function(event) {
event.preventDefault(); // prevent page reload
console.log("Form submitted with:");
console.log("Username:", usernameInput.value);
console.log("Email:", emailInput.value);
console.log("Password:", passwordInput.value);
});
</script>
</body>
</html>
דוגמה 3 - קיצורי מקלדת¶
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Keyboard Shortcuts</title>
<style>
body { font-family: Arial, sans-serif; padding: 20px; }
#modal { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
background: white; padding: 30px; border: 2px solid #333; border-radius: 10px;
box-shadow: 0 5px 30px rgba(0,0,0,0.3); z-index: 100; }
#overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5); z-index: 99; }
#log { border: 1px solid #ddd; padding: 10px; height: 200px; overflow-y: auto; }
.key-display { font-family: monospace; background: #eee; padding: 2px 6px; border-radius: 3px; border: 1px solid #ccc; }
</style>
</head>
<body>
<h1>Keyboard Shortcuts Demo</h1>
<p>Try these shortcuts:</p>
<ul>
<li><span class="key-display">Ctrl+K</span> - Open search modal</li>
<li><span class="key-display">Escape</span> - Close modal</li>
<li><span class="key-display">Ctrl+B</span> - Toggle bold text</li>
</ul>
<p id="styled-text">This text can be toggled bold with Ctrl+B</p>
<div id="log"></div>
<div id="overlay"></div>
<div id="modal">
<h2>Search</h2>
<input type="text" id="search-input" placeholder="Search...">
</div>
<script>
const modal = document.getElementById("modal");
const overlay = document.getElementById("overlay");
const searchInput = document.getElementById("search-input");
const styledText = document.getElementById("styled-text");
const log = document.getElementById("log");
function addToLog(message) {
const entry = document.createElement("div");
entry.textContent = new Date().toLocaleTimeString() + " - " + message;
log.prepend(entry);
}
function openModal() {
modal.style.display = "block";
overlay.style.display = "block";
searchInput.focus();
addToLog("Modal opened (Ctrl+K)");
}
function closeModal() {
modal.style.display = "none";
overlay.style.display = "none";
addToLog("Modal closed (Escape)");
}
document.addEventListener("keydown", function(event) {
// Ctrl+K - open search
if ((event.ctrlKey || event.metaKey) && event.key === "k") {
event.preventDefault();
openModal();
}
// Escape - close modal
if (event.key === "Escape") {
closeModal();
}
// Ctrl+B - toggle bold
if ((event.ctrlKey || event.metaKey) && event.key === "b") {
event.preventDefault();
if (styledText.style.fontWeight === "bold") {
styledText.style.fontWeight = "normal";
} else {
styledText.style.fontWeight = "bold";
}
addToLog("Bold toggled (Ctrl+B)");
}
});
// close modal when clicking overlay
overlay.addEventListener("click", closeModal);
</script>
</body>
</html>
סיכום סוגי אירועים¶
| קטגוריה | אירועים |
|---|---|
| עכבר | click, dblclick, mousedown, mouseup, mouseenter, mouseleave, mouseover, mouseout, mousemove, contextmenu |
| מקלדת | keydown, keyup |
| פוקוס | focus, blur, focusin, focusout |
| טופס | submit, change, input, reset |
| חלון | load, DOMContentLoaded, resize, scroll, beforeunload |
| לוח | copy, cut, paste |
| גרירה | drag, dragstart, dragend, dragover, dragleave, drop |
| מגע | touchstart, touchmove, touchend |
סיכום אובייקט האירוע¶
| מאפיין/מתודה | תיאור |
|---|---|
event.type |
סוג האירוע |
event.target |
האלמנט שהפעיל את האירוע |
event.currentTarget |
האלמנט שה-listener רשום עליו |
event.preventDefault() |
ביטול התנהגות ברירת מחדל |
event.stopPropagation() |
עצירת בעבוע |
event.clientX/Y |
מיקום עכבר ביחס לחלון |
event.pageX/Y |
מיקום עכבר ביחס לדף |
event.key |
שם המקש שנלחץ |
event.code |
קוד המקש הפיזי |
event.altKey/ctrlKey/shiftKey/metaKey |
מקשי modifier |
event.button |
כפתור עכבר (0=שמאל, 1=אמצע, 2=ימין) |
- תמיד נשתמש ב-
addEventListener(לא inline handlers) - אובייקט האירוע מכיל את כל המידע שצריך על מה שקרה
preventDefaultמבטל את ההתנהגות הרגילה (שליחת טופס, ניווט לקישור)stopPropagationעוצר את בעבוע האירוע להורים (נלמד בהרחבה בשיעור הבא)- אפשרות
once: trueשימושית כשצריך handler חד-פעמי