
מבוא לאירועים בג'אווה סקריפט
אירועים (events) הם איתותים שמשהו קרה בדפדפן האינטרנט. הם חיוניים להפיכת דפי אינטרנט לדינמיים ואינטראקטיביים. לדוגמה, אירועים מופעלים כאשר משתמש לוחץ על כפתור, שולח טופס, לוחץ על מקש או מקליק עם העכבר.
ב-JavaScript ניתן לזהות אירועים שקורים ולהפעיל קטעי קוד ברגע שהם מתרחשים. נוכל להשתמש בפונקציית האזנה (Event Listener), כדי "להאזין" לאלמנט כלשהו במסמך האינטרנט ולהמתין לרגע שאירוע מסויים מתרחש עבורו.
פונקציית האזנה ניתן לקבוע באמצעות קוד JavaScript שמובנה ישירות בתוך אלמנט ה-HTML או באמצעות קוד JavaScript שמופרד מאלמנט ה-HTML. נראה שתי אפשרויות אלו להלן.
שימוש בפונקציית האזנה מובנית בקוד (inline)
זוהי הדרך הפשוטה ביותר להגדרת פונקציית האזנה לאלמנט ב-HTML, אבל היא גם פחות מומלצת ברוב המקרים, מכיוון שהיא מערבבת בין תאור מבנה המסמך (ה-HTML) לקוד הביצוע (ה-JavaScript).
דוגמה:
<button onclick="alert('Button was clicked!')">Click Me</button>
בדוגמה זו, הגדרנו האזנה לאלמנט הכפתור (button). אנו מאזינים לאירוע מסוג onclick, כלומר, הקלקה על הכפתור. לאחר שהאירוע מתרחש, כלומר הקליקו על הכפתור, מופעל הקוד במרכאות שלפיו מבוצעת פקודת alert שמקפיצה הודעה לגולש.
ניתן לראות כי קוד ה-JavaScript מוטמע כחלק מקוד ה-HTML, דבר שיוצר קוד HTML ארוך ומסורבל יותר, איטי יותר, פחות קריא וקשה יותר לתחזוקה. לכן, ברוב המקרים נעדיף שלא להשתמש בשיטה זו להאזנה לאירועים.
שימוש במתודת האזנה addEventListener
דרך נוספת להגדרת אירועים ב-JavaScript היא שימוש במתודת ההאזנה addEventListener. זוהי דרך מעט מורכבת יותר, אבל נכונה יותר לוגית, כיוון שהיא מפרידה בין קוד ה-HTML לקוד ה-JavaScript.
המתודה addEventListener מקבלת 2 פרמטרים: הפרמטר הראשון מחרוזת שמזהה את סוג האירוע שעליו אנו מאזינים והפרמטר השני הוא פונקציה שתופעל ברגע שהאירוע מתרחש. היא יכולה גם לקבל פרמטר שלישי, אבל על כך בהמשך.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event Listeners</title>
</head>
<body>
<button id="myButton">Click Me</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
alert('Button was clicked!');
});
</script>
</body>
</html>
בדוגמה זו, ניתן לראות שאלמנט הכפתור myButton אינו כולל בתוכו כל קוד JavaScript. קוד ה-JavaScript כולו מופרד ונמצא בין תגיות script. אנו מגדירים תחילה את הקבוע button כאובייקט המצביע על האלמנט של הכפתור myButton, ומפעילים ממנו את המתודה addEventListener. הארגומנט הראשון שמועבר למתודה הוא המחרוזת onclick, כלומר אנו מאזינים על אירוע הקלקה, והארגומנט השני כולל פונקציה אנונימית שמפעילה פקודת alert.
במקום שימוש בפונקציה אנונימית כארגומנט למתודה addEventListener, נוכל להשתמש ברפרנס לפונקציה עם שם.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Named Functions</title>
</head>
<body>
<button id="myButton">Click Me</button>
<script>
function showAlert() {
alert('Button was clicked!');
}
const button = document.getElementById('myButton');
button.addEventListener('click', showAlert);
</script>
</body>
</html>
בדוגמה זו, יצרנו פונקציה בשם showAlert אשר מפעילה פקודת alert. הפעם, במקום להעביר למתודה addEventListener פונקציה אנונימית בפרמטר השני, העברנו ארגומנט שהוא רפרנס לפונקציה showAlert.
היתרון בשימוש בפונקציה עם שם, הוא שניתן למחזר את השימוש בפונקציה הזו (code reusability). אם לדוגמה יש לנו מספר אירועים שבכולם מבוצע אותו הקוד או אם הקוד גם משמש במקרים נוספים (לאו דווקא כשמתרחש אירוע), נוכל לקרוא לאותה הפונקציה. באופן הזה, קוד ה-JavaScript יהיה קריא יותר, ועם פחות מקום לטעויות ובאגים.
ניתן לראות בשתי הדוגמאות האחרונות כי קוד ה-JavaScript נמצא בחלק script ייעודי, ואינו מעורבב עם תגיות ה-HTML עצמן. בדרך זו, קוד ה-HTML קריא יותר, מהיר יותר וקל יותר לתחזוקה. ברוב המקרים, ובמיוחד כאשר קוד ה-JavaScript ארוך, רצוי להעביר את חלק ה-script לקובץ JavaScript חיצוני, וכך לקבל קוד HTML נקי ומהיר עוד יותר. הסיבה שאנו הכנסנו את קוד ה-script כבלוק ב-HTML במקום להפריד אותו לקובץ חיצוני היא מטעמי נוחיות קריאת המדריך בלבד.
פיעפוע של אירועים
כאשר מוגדרות כמה פונקציות האזנה על אותו האירוע, כל אחת על אלמנט שונה, יתכן ויותר מפונקציית האזנה אחת תופעל. אם לדוגמה מוגדרים כמה אלמנטים, האחד בתוך השני, והקלקנו על האלמנט הפנימי, תופעל גם פונקציית ההאזנה שמאזינה לאירוע מסוג click של האלמנט הפנימי וגם פונקציית ההאזנה שמאזינה לאירוע מסוג click של האלמנט החיצוני.
פיעפוע (propagation) של אירועים קובע כיצד אירועים עוברים דרך עץ ה-DOM. במילים אחרות: הפיעפוע קובע את סדר הפעלת פונקציות ההאזנה השונות.
הפיעפוע מורכב משלושה שלבים:
- שלב הלכידה (capture) - האירוע מתחיל מהשורש (האלמנט החיצוני ביותר) ומחלחל מטה לאלמנט היעד (האלמנט הפנימי ביותר).
- שלב היעד (target) - האירוע מגיע לאלמנט היעד.
- שלב הביעבוע (bubbling) - האירוע מבעבע בחזרה מאלמנט היעד לשורש.
נניח שיש לנו 3 אלמנטים האחד בתוך השני: אלמנט א', בתוכו אלמנט ב' ובתוכו אלמנט ג'. ונניח שהקלקנו על אלמנט ג', הפנימי ביותר. במקרה כזה, אלמנט ג' (הפנימי ביותר) הוא היעד, ואלמנט א' (החיצוני ביותר) הוא השורש. להלן סדר האירועים שיקרו בעת ההקלקה על אלמנט ג': תופעל פונקציית ההאזנה בשלב הלכידה עבור א', תופעל פונקציית ההאזנה בשלב הלכידה עבור ב', תופעל פונקציית ההאזנה בשלב הלכידה עבור ג', יופעל אירוע ברירת המחדל של ג' (היעד), תופעל פונקציית ההאזנה בשלב הביעבוע עבור ג', תופעל פונקציית ההאזנה בשלב הביעבוע עבור ב' ותופעל פונקציית ההאזנה בשלב הביעבוע עבור א'. כלומר, ירדנו מהשורש א' לעבר היעד ג' ולאחר מכן עלינו חזרה מהיעד ג' לשורש א'.
כברירת מחדל, בהגדרת פונקציית האזנה addEventListener, הפונקצייה תופעל בשלב הביעבוע. נוכל להעביר לפונקציה פרמטר שלישי בוליאני (true או false) שקובע האם פונקציית ההאזנה תופעל בשלב הלכידה. אם הפרמטר השלישי הוא true, פונקציית ההאזנה תופעל כבר בשלב הלכידה. אם הפרמטר השלישי הוא false תופעל ברירת המחדל והפונקציה תופעל בשלב הביעבוע.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Event Propagation Example</title>
<style>
.outer {
padding: 50px;
background-color: lightblue;
}
.middle {
padding: 50px;
background-color: lightcoral;
}
.inner {
padding: 50px;
background-color: lightgreen;
}
</style>
</head>
<body>
<div class="outer">
Outer
<div class="middle">
Middle
<div class="inner">
Inner
</div>
</div>
</div>
<script>
document.querySelector('.outer').addEventListener('click', (event) => {
console.log('Outer Div - Bubbling Phase');
});
document.querySelector('.middle').addEventListener('click', (event) => {
console.log('Middle Div - Bubbling Phase');
});
document.querySelector('.inner').addEventListener('click', (event) => {
console.log('Inner Div - Bubbling Phase');
});
document.querySelector('.outer').addEventListener('click', (event) => {
console.log('Outer Div - Capturing Phase');
}, true);
document.querySelector('.middle').addEventListener('click', (event) => {
console.log('Middle Div - Capturing Phase');
}, true);
document.querySelector('.inner').addEventListener('click', (event) => {
console.log('Inner Div - Capturing Phase');
}, true);
// Output when clicking on the element 'inner'
// Outer Div - Capturing Phase
// Middle Div - Capturing Phase
// Inner Div - Capturing Phase
// Inner Div - Bubbling Phase
// Middle Div - Bubbling Phase
// Outer Div - Bubbling Phase
</script>
</body>
</html>
בדוגמה זו, יצרנו 3 אלמנטים אחד בתוך השני: חיצוני outer, אמצעי middle ופנימי inner. הגדרנו 6 פונקציות האזנה, מהן 3 הראשונות תופעלנה בשלב הביעבוע (כיוון שלא העברנו פרמטר שלישי למתודה addEventListener) ו-3 האחרונות תופעלנה בשלב הלכידה (כיוון שהעברנו פרמטר שלישי עם הערך true). בעת הקלקה על האלמנט הפנימי inner, תופעלנה כל 6 הפונקציות בסדר הפיעפוע ויודפס לקונסול סדר הפיעפוע.
עצירת פיעפוע אירועים - המתודה stopPropagation
המתודה stopPropagation עוצרת את המשך הפיעפוע של האירועים.
דוגמה:
<button id="stopButton">Stop Propagation</button>
<script>
const stopButton = document.getElementById('stopButton');
stopButton.addEventListener('click', (event) => {
event.stopPropagation();
alert('Propagation stopped');
});
document.body.addEventListener('click', () => {
alert('Body clicked');
});
</script>
בדוגמה זו, הקלקה על הכפתור stopButton תפעיל את פונקציית ההאזנה הרלוונטית של הכפתור. פונקציית ההאזנה כוללת קריאה למתודה stopPropagation, ולכן, יעצר המשך הפיעפוע של האירוע, ולא תופעל פונקציית ההאזנה של אלמנט ה-body, שנמצאת באותו עץ DOM של stopButton ומאזינה לאותו האירוע.
חסימת התנהגות ברירת מחדל לאירועים - המתודה preventDefault
כאשר אירוע מופעל, לעתים קרובות יש לו התנהגות ברירת מחדל הקשורה אליו. לדוגמה, לחיצה על קישור בדף אינטרנט בדרך כלל מנווטת לכתובת URL חדשה, ושליחת טופס בדרך כלל שולחת נתוני טופס לשרת. באמצעות preventDefault, ניתן למנוע התנהגויות ברירת מחדל אלה וליישם התנהגות מותאמת אישית במקום זאת.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prevent Default Example</title>
</head>
<body>
<form id="myForm">
<label for="username">Username:</label>
<input type="text" id="username" name="username">
<button type="submit">Submit</button>
</form>
<script>
document.getElementById('myForm').addEventListener('submit', function(event) {
const username = document.getElementById('username').value;
if (username === '') {
alert('Username cannot be empty!');
event.preventDefault(); // Prevent form submission
}
});
</script>
</body>
</html>
בדוגמה זו, אם שדה שם המשתמש ריק, הטופס לא יישלח, ובמקום זאת תוצג הודעת התראה.
דוגמה נוספת:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prevent Default Example</title>
</head>
<body>
<a href="https://example.com" id="myLink">Go to Example.com</a>
<script>
document.getElementById('myLink').addEventListener('click', function(event) {
event.preventDefault(); // Prevent navigation
alert('Link clicked, but navigation prevented!');
});
</script>
</body>
</html>
בדוגמה זו, הקלקה על הקישור תפעיל את הודעת ההתראה ולא תנווט לכתובת שהוגדרה בקישור.
המתודה preventDefault שימושית במיוחד בתרחישים שבהם צריך לאמת נתונים לפני ביצוע פעולה או כאשר רוצים לספק חוויית משתמש טובה יותר באמצעות הגדרת פונקציונליות מותאמת אישית המחליפה את התנהגות ברירת המחדל.
אפשרויות מתקדמות למתודה addEventListener
ניתן להעביר למתודה addEventListener פרמטר נוסף, שלישי, עם רשימת אפשרויות, אשר מספקות הגדרות שונות לאופן ביצוע המתודה. העברת פרמטר שלישי עם אפשרויות הוא חלופה להעברת פרמטר שלישי בוליאני שקובע את סדר הפיעפוע (כפי שהוסבר לעיל בחלק שמדבר על פיעפוע של אירועים).
נסקור את האפשרויות הנפוצות: once, capture, passive. רשימת האפשרויות המועברת כפרמטר שלישי יכולה לכלול אפשרות אחת יחידה או לשלב בין כמה מהאפשרויות הללו.
האפשרות once
האפשרות once מבטיחה שפונקציית ההאזנה תבוצע פעם אחת בלבד ולאחר מכן תוסר אוטומטית. הדבר יכול להיות שימושי עבור אירועים שיש לטפל בהם רק פעם אחת, כגון פעולות הגדרה ראשוניות או התראות חד פעמיות.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Once Option Example</title>
</head>
<body>
<button id="onceButton">Click Me Once</button>
<script>
const button = document.getElementById('onceButton');
button.addEventListener('click', () => {
alert('This alert will show only once.');
}, { once: true });
</script>
</body>
</html>
בדוגמה זו, הקלקת עכבר על הכפתור onceButton תפעיל פונקציית alert שמקפיצה הודעה לגולש. הודעה זו תופעל פעם אחת בלבד בהקלקה הראשונה, ולאחר מכן פונקציית ההאזנה לא תפעל יותר.
האפשרות capture
האפשרות capture מבטיחה שפונקציית ההאזנה תופעל במהלך שלב הלכידה, כלומר מיד כשהאירוע מתרחש ולפני הפעלת הטיפול באירוע על ידי פונקציות האזנה נוספות שאולי קיימות לאלמנטים נוספים בעץ ה-DOM ומאזינות לאותו האירוע. העברת האפשרות capture עם ערך בוליאני true או false זהה להעברת פרמטר שלישי בוליאני עם אותו הערך למתודה addEventListener (כפי שהוסבר לעיל בחלק המדבר על פיעפוע של אירועים).
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Capture Option Example</title>
</head>
<body>
<div id="outerDiv" style="padding: 50px; background: lightgray;">
Outer Div
<div id="innerDiv" style="padding: 20px; background: lightblue;">
Inner Div
</div>
</div>
<script>
const outerDiv = document.getElementById('outerDiv');
const innerDiv = document.getElementById('innerDiv');
outerDiv.addEventListener('click', () => {
console.log('Outer Div (capturing)');
}, { capture: true });
innerDiv.addEventListener('click', () => {
console.log('Inner Div (bubbling)');
});
</script>
</body>
</html>
בדוגמה זו, הגדרנו שני אלמנטים זה בתוך זה: האלמנט החיצוני outerDiv ובתוכו האלמנט innerDiv. ל-2 האלמנטים הגדרנו פונקציית האזנה על אירוע הקלקה (click). פונקציית ההאזנה של innerDiv אינה כוללת פרמטר שלישי, ולכן היא מופעלת בשלב הביעבוע. פונקציית ההאזנה של outerDiv כוללת פרמטר שלישי capture עם הערך true, ולכן היא מופעלת בשלב הלכידה. בהתאם להגדרות אילו, הקלקה על האלמנט innerDiv תבצע פיעפוע של האירוע click באופן הבא: קודם תופעל פונקציית ההאזנה של outerDiv (כיוון שהיא מוגדרת בשלב הלכידה) ולאחריה תופעל פונקציית ההאזנה של innerDiv (כיוון שהיא מוגדרת בשלב הביעבוע). אם לא היינו מעבירים פרמטר שלישי לפונקציית ההאזנה outerDiv, סדר ביצוע פונקציות ההאזנה היה הפוך, כיוון ששתי פונקציות ההאזנה מופעלות בשלב הביעבוע ולכן מופעלת קודם פונקציית ההאזנה של היעד (innerDiv) ולאחר מכן של השורש (outerDiv).
האפשרות passive
האפשרות passive מבטיחה שפונקציית ההאזנה לא תפעיל את המתודה preventDefault. אפשרות זו שימושית בעיקר כאופטימיזציה לביצועים, במיוחד עבור אירועי מגע (touch events) בסלולר ואירועי גלילה (scroll events) של העמוד, שבהם מניעת התנהגות ברירת מחדל עלולה להוביל לבעיות ביצועים.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Passive Option Example</title>
</head>
<body>
<div id="scrollableDiv" style="height: 100px; overflow-y: scroll;">
<div style="height: 1000px;">Scroll me</div>
</div>
<script>
const scrollableDiv = document.getElementById('scrollableDiv');
scrollableDiv.addEventListener('scroll', () => {
console.log('Scrolling...');
}, { passive: true });
</script>
</body>
</html>
בדוגמה זו, הגדרנו פונקציית האזנה על האלמנט scrollableDiv. כיוון שמדובר בהאזנה לאירוע מסוג scroll, העברנו את האפשרות passive עם הערך true. בכך יידענו את הדפדפן שלא תופעל בהמשך המתודה preventDefault, ושיפרנו את מהירות הטיפול באירוע הגלילה.
שילוב מספר אפשרויות יחד
ניתן לשלב יותר מאפשרות אחת למתודה addEventListener.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Combined Event Listener Options</title>
</head>
<body>
<div id="container" style="padding: 50px; background: lightgray;">
Clickable Area
</div>
<script>
const container = document.getElementById('container');
container.addEventListener('click', (event) => {
console.log('Container clicked (once, capturing, passive)');
}, { once: true, capture: true, passive: true });
</script>
</body>
</html>
בדוגמה זו, הגדרנו פונקציית האזנה על האלמנט container, אשר מאזינה לאירוע הקלקת עכבר. בגלל הערך once, פונקציית ההאזנה תופעל פעם אחת בלבד ולאחר מכן תוסר. בגלל הערך capture, הפונקציה תפעיל את קוד האירוע מיד בזיהוי האירוע בשלב הלכידה, לפני קודי טיפול אחרים באירוע זה. בגלל הערך passive, הדרכנו את הדפדפן שלא תופעל מתודת preventDefault והאצנו את הטיפול בהקלקה.
טיפול באירועים נפוצים בג'אווה סקריפט
אירועי עכבר
click - מופעל כאשר לוחצים על אלמנט.
dblclick - מופעל כאשר לוחצים לחיצה כפולה על אלמנט.
mouseover - מופעל כאשר מצביע העכבר נמצא מעל אלמנט.
mouseout - מופעל כאשר מצביע העכבר עוזב אלמנט.
mousemove - מופעל כאשר מצביע העכבר נע בתוך אלמנט.
דוגמה:
<div id="box" style="width: 100px; height: 100px; background: red;"></div>
<script>
const box = document.getElementById('box');
box.addEventListener('click', () => alert('Box clicked!'));
box.addEventListener('mouseover', () => box.style.background = 'blue');
box.addEventListener('mouseout', () => box.style.background = 'red');
box.addEventListener('mousemove', () => console.log('Mouse moved'));
</script>
אירועי מקלדת
keydown - מופעל כאשר לוחצים על מקש.
keyup - מופעל כאשר משחררים לחיצה על מקש.
keypress - מופעל כאשר לוחצים על מקש ומשחררים את הלחיצה.
דוגמה:
<input type="text" id="inputField" placeholder="Type something...">
<script>
const inputField = document.getElementById('inputField');
inputField.addEventListener('keydown', (event) => {
console.log(`Key down: ${event.key}`);
});
inputField.addEventListener('keyup', (event) => {
console.log(`Key up: ${event.key}`);
});
</script>
אירועי טפסים
submit - מופעל כאשר טופס נשלח.
change - מופעל כאשר הערך של אלמנט קלט משתנה.
focus - מופעל כאשר אלמנט מקבל מיקוד.
blur - מופעל כאשר אלמנט מאבד מיקוד.
input - מופעל כאשר הערך של רכיב קלט משתנה.
דוגמה:
<form id="myForm">
<input type="text" name="username" placeholder="Username" required>
<button type="submit">Submit</button>
</form>
<script>
const form = document.getElementById('myForm');
form.addEventListener('submit', (event) => {
event.preventDefault(); // Prevent form from submitting
alert('Form submitted!');
});
form.querySelector('input').addEventListener('input', (event) => {
console.log(`Input changed to: ${event.target.value}`);
});
</script>
אירועי נגיעה (touch)
touchstart - מופעל כאשר נקודת מגע מונחת על משטח המגע.
touchend - מופעל כאשר נקודת מגע מוסרת ממשטח המגע.
touchmove - מופעל כאשר נקודת מגע נעה לאורך משטח המגע.
touchcancel - מופעל כאשר אירוע מגע מופרע.
דוגמה:
<div id="touchArea" style="width: 100px; height: 100px; background: yellow;"></div>
<script>
const touchArea = document.getElementById('touchArea');
touchArea.addEventListener('touchstart', () => {
console.log('Touch started');
});
touchArea.addEventListener('touchend', () => {
console.log('Touch ended');
});
touchArea.addEventListener('touchmove', () => {
console.log('Touch moved');
});
touchArea.addEventListener('touchcancel', () => {
console.log('Touch canceled');
});
</script>
אירוע גלילה
scroll - מופעל כאשר האלמנט או המסמך עצמו נגללים.
דוגמה:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Scroll Event Example</title>
<style>
#scrollable {
width: 300px;
height: 150px;
overflow: auto;
border: 1px solid black;
padding: 10px;
}
.content {
width: 600px;
height: 300px;
background: linear-gradient(to bottom, #ffcccc, #ccffff);
}
</style>
</head>
<body>
<div id="scrollable">
<div class="content">
Scroll inside this box to see the scroll event in action.
</div>
</div>
<script>
const scrollable = document.getElementById('scrollable');
// Scroll event listener
scrollable.addEventListener('scroll', () => {
console.log('Scrolling...');
console.log('scrollTop:', scrollable.scrollTop);
console.log('scrollLeft:', scrollable.scrollLeft);
});
</script>
</body>
</html>
האצלת אירועים (Event Delegation)
האצלת אירועים היא טכניקה הכוללת הגדרת פונקציית האזנה בודדה לאלמנט אב, כדי לנהל אירועים עבור מספר אלמנטים צאצאים. הדבר יעיל ומסייע בניהול תוכן דינמי.
דוגמה:
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
<script>
const list = document.getElementById('list');
list.addEventListener('click', (event) => {
if (event.target.tagName === 'LI') {
alert(`You clicked on ${event.target.textContent}`);
}
});
</script>
בדוגמה זו, הגדרנו פונקציית האזנה יחידה עבור אלמנט האב list. בכל הקלקה על אחד האלמנטים מסוג li, מופעלת פונקציית ההאזנה הזו, בגלל שיטת הפיעפוע שמפעפעת את האירוע מאלמנטי ה-li מהשורש list ליעד li ובחזרה. בכל פעם שפונקציית ההאזנה מופעלת, אנו נעזרים במאפיין target שמחזיר את אלמנט היעד (li) ובודקים את מאפיין ה-tagName שלו שמחזיר את סוג האלמנט. אם סוג האלמנט הוא li, תבוצע פקודת ה-alert.
מחיקת אירועים - המתודה removeEventListener
ניתן להסיר פונקציית האזנה לאירועים באמצעות המתודה removeEventListener. הדבר שימושי לניקוי ומניעת דליפות זיכרון.
דוגמה:
<button id="start">Start</button>
<button id="stop">Stop</button>
<script>
const startButton = document.getElementById('start');
const stopButton = document.getElementById('stop');
function handleClick() {
alert('Button clicked!');
}
startButton.addEventListener('click', handleClick);
stopButton.addEventListener('click', () => {
startButton.removeEventListener('click', handleClick);
alert('Event listener removed');
});
</script>
בדוגמה זו, הקלקה על הכפתור start מפעילה את פונקציית ההאזנה של הכפתור, אשר מקפיצה הודעת לגולש באמצעות פקודת alert. הקלקה על הכפתור stop מוחקת את פונקציית ההאזנה שמקפיצה את ההודעה, כך שבהקלקות נוספות לאחר מכן על הכפתור start לא תקפוץ יותר הודעה.
אירועים מותאמים אישית - המתודה dispatchEvent
ניתן ליצור אירועים מותאמים אישית. הדבר יכול להיות שימושי ליצירת קוד מודולרי שנעשה בו שימוש חוזר. לדוגמה, אם נרצה שגם הקלקה על מקש מסויים וגם הקלקה עם העכבר על אלמנט מסויים יפעילו שניהם את אותו אירוע מותאם אישית שהמצאנו.
דוגמה:
<div id="customEventDiv">Click me to trigger a custom event</div>
<script>
const div = document.getElementById('customEventDiv');
// Create a custom event
const customEvent = new Event('myCustomEvent');
// Add an event listener for the custom event
div.addEventListener('myCustomEvent', () => {
alert('Custom event triggered!');
});
// Dispatch the custom event on click
div.addEventListener('click', () => {
div.dispatchEvent(customEvent);
});
</script>
בדוגמה זו, המצאנו אירוע מותאם אישית בשם myCustomEvent והגדרנו פונקציית האזנה עבורו, אשר מקפיצה הודעה לגולש באמצעות פקודת alert. כמו כן, יצרנו פונקציית האזנה לאלמנט customEventDiv שמאזינה לאירוע הקלקה. בעת הקלקה על האלמנט customEventDiv מופעלת פונקציית ההאזנה על האלמנט, אשר מבצעת dispatchEvent ולמעשה "משגרת" את האירוע myCustomEvent שמפעיל את פונקציית ההאזנה שלו.
קביעת תכיפות אירועים - Debouncing and Throttling
טכניקת debouncing וטכניקת throttling הן טכניקות המשמשות להגבלת הקצב שבו פונקציה מבוצעת, במיוחד בתגובה לאירועים שעלולים להיות מופעלים לעתים קרובות, כגון אירוע scroll (גלילת העמוד) או אירוע resize (שינוי גודל החלון).
טכניקת debouncing
טכניקת debouncing מבטיחה שפונקציה נקראת פעם אחת בלבד לאחר שחלף פרק זמן מסוים מאז הפעלתו האחרונה.
דוגמה:
<input type="text" id="debouncedInput" placeholder="Type something...">
<script>
const input = document.getElementById('debouncedInput');
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
function handleInput(event) {
console.log(`Debounced input: ${event.target.value}`);
}
input.addEventListener('input', debounce(handleInput, 300));
</script>
בדוגמה זו, הגדרנו שדה קלט בשם debouncedInput. כל הכנסת קלט לשדה הזה מפעילה אירוע מסוג input. נרצה שבכל הכנסת קלט לשדה, תופעל הפונקציה handleInput, אשר מדפיסה בקונסול שהופעל את ערך הקלט שהוכנס. עם זאת, לא נרצה להציף את הקונסול במלא הודעות. לכן, הגדרנו פונקציית האזנה על שדה הקלט debouncedInput, אבל במקום שפונקציית ההאזנה תפעיל ישירות את הפונקציה handleInput בעת האירוע input, הפעלנו את הפונקציה debounce שיצרנו עם 2 פרמטרים: רפרנס לפונקציה handleInput והערך 300. הפונקציה debounce מבצעת המתנה של פרק זמן מסויים (300 אלפיות השניה במקרה שלנו) ולאחריו מפעילה את הפונקציה handleInput. באופן הזה, האטנו את קצב הפעלות הפונקציה handleInput לקצב של לכל היותר הפעלה כל 300 אלפיות שניה.
טכניקת throttling
טכניקת throttling מבטיחה שפונקציה נקראת רק פעם אחת בלבד בפרק זמן מוגדר.
דוגמה:
<div id="scrollContainer" style="height: 200px; overflow: auto;">
<div style="height: 1000px;">Scroll me</div>
</div>
<script>
const scrollContainer = document.getElementById('scrollContainer');
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
if (!lastRan) {
func.apply(this, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if (Date.now() - lastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
function handleScroll(event) {
console.log('Throttled scroll event');
}
scrollContainer.addEventListener('scroll', throttle(handleScroll, 200));
</script>
בדוגמה זו, הגדרנו אלמנט scrollContainer. נרצה שבכל אירוע scroll באלמנט הזה תופעל הפונקציה handleScroll, אשר מדפיסה בקונסול שנעשתה גלילה. עם זאת, לא נרצה להציף את הקונסול במלא הודעות. לכן, הגדרנו פונקציית האזנה על האלמנט scrollContainer, אבל במקום שפונקציית ההאזנה תפעיל את הפונקציה handleScroll בעת האירוע scroll, הפעלנו את הפונקציה throttle שיצרנו עם 2 פרמטרים: רפרנס לפונקציה handleScroll והערך 200. הפונקציה throttle מחזיקה במשתנה lastRan את זמן ההפעלה האחרון של הפונקציה handleScroll. בכל קריאה ל-throttle, מופעלת הפונקציה handleScroll בצורה מושהית (כלומר לא באופן מיידי), בעוד פרק זמן של לפחות 200 אלפיות השניה מהפעם הקודמת בה הופעלה. באופן הזה, מובטח שבין כל הפעלה של handleScroll אחת לאחרת, עברו לפחות 200 אלפיות שניה.