ניתוח מעמיק של אירוע JavaScript

preventDefault ו-stopPropagation: מתי משתמשים בכל אחת מהן ומה בדיוק כל אחת מהן עושה.

טיפול באירועים ב-JavaScript הוא לרוב פשוט. זה נכון במיוחד כשמדובר במבנה HTML פשוט (יחסית שטוח). עם זאת, כשאירועים עוברים (או מתפשטים) דרך היררכיה של רכיבים, הדברים נעשים קצת יותר מורכבים. בדרך כלל, המפתחים פונים אל stopPropagation() ו/או preventDefault() כדי לפתור את הבעיות שהם נתקלים בהן. אם חשבתם פעם "אנסה את preventDefault() ואם זה לא עובד אנסה את stopPropagation() ואם זה לא עובד אנסה את שניהם", המאמר הזה בשבילכם! אסביר בדיוק מה כל שיטה עושה, מתי להשתמש בכל אחת מהן ואציג מגוון דוגמאות עובדות לבדיקה. המטרה שלי היא לסיים את הבלבול שלך אחת ולתמיד.

לפני שנמשיך, חשוב להתייחס בקצרה לשני הסוגים של טיפול באירועים שאפשר לבצע ב-JavaScript (כלומר בכל הדפדפנים המודרניים – Internet Explorer לפני גרסה 9 לא תמך בכלל בתיעוד אירועים).

סגנונות של אירועים (תיעוד ותהליך ההעברה של אירועים)

כל הדפדפנים המודרניים תומכים בתיעוד אירועים, אבל מפתחים משתמשים בתיעוד אירועים רק לעיתים רחוקות מאוד. באופן מעניין, זו הייתה הדרך היחידה לאירועים ש-Netscape תמכה בה במקור. היריב הגדול ביותר של Netscape, Microsoft Internet Explorer, לא תמך בכלל בתיעוד אירועים, אלא רק בסגנון אחר של אירועים שנקרא 'העברת אירועים'. כשה-W3C הוקם, הם מצאו יתרונות בשני הסגנונות של אירועים והצהירו שדפדפנים צריכים לתמוך בשניהם, באמצעות פרמטר שלישי ל-method‏ addEventListener. במקור, הפרמטר הזה היה רק בוליאני, אבל כל הדפדפנים המודרניים תומכים באובייקט options בתור הפרמטר השלישי, שאפשר להשתמש בו כדי לציין (בין היתר) אם רוצים להשתמש בתיעוד אירועים או לא:

someElement.addEventListener('click', myClickHandler, { capture: true | false });

שימו לב שהאובייקט options הוא אופציונלי, וכך גם המאפיין capture שלו. אם אחד מהם לא יצוין, ערך ברירת המחדל של capture יהיה false, כלומר המערכת תשתמש באירוע bubbling.

תיעוד אירועים

מה המשמעות של 'האזנה בשלב הצילום' בגורם מטפל באירועים? כדי להבין את זה, אנחנו צריכים לדעת איך האירועים נוצרים ואיך הם עוברים. הכלל הזה רלוונטי לכל האירועים, גם אם אתם, בתור מפתחים, לא משתמשים בהם, לא מתעניינים בהם או לא חושבים עליהם.

כל האירועים מתחילים בחלון ועוברים קודם את שלב הצילום. כלומר, כשאירוע נשלח, הוא מתחיל את החלון ונוסע קודם "למטה" לעבר רכיב היעד שלו. זה קורה גם אם אתם מקשיבים רק בשלב הבועות. הנה דוגמה למטא-תגים ול-JavaScript:

<html>
 
<body>
   
<div id="A">
     
<div id="B">
       
<div id="C"></div>
     
</div>
   
</div>
 
</body>
</html>
document.getElementById('C').addEventListener(
 
'click',
 
function (e) {
    console
.log('#C was clicked');
 
},
 
true,
);

כשמשתמש לוחץ על רכיב #C, נשלח אירוע שמקורו ב-window. לאחר מכן, האירוע הזה יעבור אל הצאצאים שלו באופן הבא:

window => document => <html> => <body> => וכן הלאה, עד שהיא מגיעה ליעד.

לא משנה אם אין מכשיר שמקשיב לאירוע קליק ברכיב window או ברכיב document או ברכיב <html> או ברכיב <body> (או בכל רכיב אחר בדרך ליעד שלו). האירוע עדיין מתחיל ב-window ומתחיל את המסע שלו כפי שמתואר למעלה.

בדוגמה שלנו, אירוע הקליק יתפשט (זו מילה חשובה כי היא תקשר ישירות לאופן שבו פועלת השיטה stopPropagation(), ונרחיב על כך בהמשך המאמר) מ-window אל רכיב היעד שלו (במקרה הזה, #C) דרך כל רכיב בין window ל-#C.

פירוש הדבר הוא שאירוע הקליק יתחיל ב-window והדפדפן ישאל את השאלות הבאות:

"האם יש משהו שמקשיב לאירוע לחיצה על window בשלב הצילום?" במקרה כזה, גורמי הטיפול באירועים המתאימים יופעלו. בדוגמה שלנו, אף אחד מהם לא מוגדר, ולכן אף בורר לא יופעל.

לאחר מכן, האירוע יתפשט אל document והדפדפן ישאל: "האם יש משהו שמקשיב לאירוע לחיצה ב-document בשלב הצילום?" אם כן, גורמי הטיפול באירועים המתאימים יופעלו.

לאחר מכן, האירוע יתפשט לאלמנט <html> והדפדפן ישאל: "האם יש משהו שמקשיב לקליק על אלמנט <html> בשלב הצילום?" אם כן, גורמי הטיפול באירועים המתאימים יופעלו.

לאחר מכן, האירוע יתפשט לאלמנט <body> והדפדפן ישאל: "האם יש משהו שמקשיב לאירוע קליק על אלמנט <body> בשלב הצילום?" אם כן, גורמי הטיפול באירועים המתאימים יופעלו.

לאחר מכן, האירוע יתפשט לאלמנט #A. שוב, הדפדפן ישאל: "האם יש משהו שמקשיב לאירוע לחיצה ב-#A בשלב הצילום, ואם כן, פונקציות הטיפול באירועים המתאימות יופעלו.

לאחר מכן, האירוע יתפשט לרכיב #B (ותוצג אותה שאלה).

לבסוף, האירוע יגיע ליעד שלו והדפדפן ישאל: "האם יש משהו שמקשיב לאירוע קליק על האלמנט #C בשלב הצילום?" הפעם התשובה היא 'כן!' פרק הזמן הקצר שבו האירוע ב היעד נקרא 'שלב היעד'. בשלב הזה, פונקציית הטיפול באירוע תופעל, הדפדפן יפרסם את הקוד console.log‏ "#C was clicked" ואז נהיה מוכנים, נכון? לא נכון! אנחנו לא סיימנו בכלל. התהליך ממשיך, אבל עכשיו הוא עובר לשלב הבועות.

אירועים שמתרחשים ברמה העליונה

יוצג בדפדפן השאלה הבאה:

"האם יש משהו שמקשיב לאירוע לחיצה ב-#C בשלב ההעברה (bubbling)?" חשוב לשים לב כאן. אפשר להאזין לקליקים (או לכל סוג אירוע אחר) גם בשלב הצילום וגם בשלב ההעברה. אם מחברים בוררים לאירועים בשני השלבים (למשל, על ידי קריאה ל-.addEventListener() פעמיים, פעם עם capture = true ופעם עם capture = false), אז כן, שני הבוררים לאירועים יופעלו באותו רכיב. עם זאת, חשוב גם לציין שהם מופעלים בשלבים שונים (אחד בשלב הצילום והשני בשלב ההעברה לטיפול ברמה הבאה).

לאחר מכן, האירוע יתפשט (המונח הנפוץ יותר הוא 'התרחבות' כי נראה שהאירוע נע "מעלה" בעץ ה-DOM) לרכיב ההורה שלו, #B, והדפדפן ישאל: "האם יש משהו שמקשיב לאירועי קליק ב-#B בשלב ההתרחבות?" בדוגמה שלנו, אף אחד מהם לא מוגדר, ולכן אף בורר לא יופעל.

לאחר מכן, האירוע יעלה אל #A והדפדפן ישאל: "האם יש משהו שמקשיב לאירועי לחיצה ב-#A בשלב ההעברה (bubbling)?"

לאחר מכן, האירוע יעלה אל <body>: "האם יש משהו שמקשיב לאירועי קליקים על הרכיב <body> בשלב ההעברה (bubbling)?"

בשלב הבא, האלמנט <html>: "האם יש משהו שמקשיב לאירועי קליקים באלמנט <html> בשלב הבועה?

השלב הבא הוא document: "Is anything listening for click events on the document in the bubbling phase?‎"

לבסוף, ה-window: "האם יש משהו שמקשיב לאירועי לחיצה בחלון בשלב הבועה?"

סוף סוף! זה היה מסע ארוך, והאירוע שלנו בטח עייף מאוד עכשיו, אבל תאמינו או לא, זה המסע שכל אירוע עובר! ברוב המקרים, הבעיה הזו לא מתגלה כי בדרך כלל המפתחים מתעניינים רק בשלב אחד של האירוע (בדרך כלל בשלב הבועות).

כדאי להקדיש קצת זמן לניסוי בתיעוד אירועים ובמעבר אירועים לטיפול ברמה גבוהה יותר, ולתעד הערות במסוף בזמן שהמטפלים מופעלים. מאוד מעניין לראות את הנתיב של אירוע. זו דוגמה להקשבה לכל האלמנטים בשני השלבים.

<html>
 
<body>
   
<div id="A">
     
<div id="B">
       
<div id="C"></div>
     
</div>
   
</div>
 
</body>
</html>
document.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on document in capturing phase');
 
},
 
true,
);
// document.documentElement == <html>
document
.documentElement.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on <html> in capturing phase');
 
},
 
true,
);
document
.body.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on <body> in capturing phase');
 
},
 
true,
);
document
.getElementById('A').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #A in capturing phase');
 
},
 
true,
);
document
.getElementById('B').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #B in capturing phase');
 
},
 
true,
);
document
.getElementById('C').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #C in capturing phase');
 
},
 
true,
);

document
.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on document in bubbling phase');
 
},
 
false,
);
// document.documentElement == <html>
document
.documentElement.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on <html> in bubbling phase');
 
},
 
false,
);
document
.body.addEventListener(
 
'click',
 
function (e) {
    console
.log('click on <body> in bubbling phase');
 
},
 
false,
);
document
.getElementById('A').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #A in bubbling phase');
 
},
 
false,
);
document
.getElementById('B').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #B in bubbling phase');
 
},
 
false,
);
document
.getElementById('C').addEventListener(
 
'click',
 
function (e) {
    console
.log('click on #C in bubbling phase');
 
},
 
false,
);

הפלט במסוף יהיה תלוי ברכיב שבו תלחצו. אם תלחצו על הרכיב 'הכי עמוק' בעץ ה-DOM (הרכיב #C), כל אחד ממטפלי האירועים האלה יופעל. הנה אלמנט #C של הפלט במסוף (עם צילום מסך גם כן), עם קצת עיצוב ב-CSS כדי להבהיר איזה אלמנט הוא איזה:

"click on document in capturing phase"
"click on <html> in capturing phase"
"click on <body> in capturing phase"
"click on #A in capturing phase"
"click on #B in capturing phase"
"click on #C in capturing phase"
"click on #C in bubbling phase"
"click on #B in bubbling phase"
"click on #A in bubbling phase"
"click on <body> in bubbling phase"
"click on <html> in bubbling phase"
"click on document in bubbling phase"

אתם יכולים לשחק עם התכונה הזו באופן אינטראקטיבי בהדגמה הפעילה שבהמשך. לוחצים על האלמנט #C ומעיינים בפלט של המסוף.

event.stopPropagation()

עכשיו, אחרי שהבנו מאיפה מגיעים האירועים ואיך הם עוברים (כלומר מתפשטים) דרך ה-DOM גם בשלב התיעוד וגם בשלב ההעברה, אפשר להפנות את תשומת הלב אל event.stopPropagation().

אפשר להפעיל את השיטה stopPropagation() באירועי DOM מקומיים (ברובם). ציינתי "רוב" כי יש כמה אירועים שבהם קריאה לשיטה הזו לא תעשה כלום (כי האירוע לא מופץ מלכתחילה). אירועים כמו focus,‏ blur,‏ load,‏ scroll ועוד כמה אירועים נכללים בקטגוריה הזו. אפשר להפעיל את stopPropagation(), אבל לא יקרה שום דבר מעניין כי האירועים האלה לא מופצים.

אבל מה stopPropagation עושה?

הוא עושה בדיוק את מה שמתואר. אחרי שתפעילו אותו, האירוע יפסיק להתפשט לאף רכיב אחר אליו הוא היה מתפשט אחרת. זה נכון בשני הכיוונים (תיעוד והעברה ל-Bubble). לכן, אם תפעילו את stopPropagation() בכל שלב בתהליך הצילום, האירוע לעולם לא יגיע לשלב היעד או לשלב ההעברה (bubbling). אם תפעילו אותו בשלב הבועות, הוא כבר יעבור את שלב הצילום, אבל הוא יפסיק "להבעבע" מהנקודה שבה הפעילתם אותו.

נחזור לאותה רכיבת Markup לדוגמה. מה לדעתכם יקרה אם נפעיל את stopPropagation() בשלב הצילום ברכיב #B?

הפלט שיוצג יהיה:

"click on document in capturing phase"
"click on <html> in capturing phase"
"click on <body> in capturing phase"
"click on #A in capturing phase"
"click on #B in capturing phase"

אתם יכולים לשחק עם התכונה הזו באופן אינטראקטיבי בהדגמה הפעילה שבהמשך. לוחצים על הרכיב #C בדמו בזמן אמת ומתבוננים בפלט של המסוף.

מה דעתך על הפסקת ההעברה ב-#A בשלב הבועות? הפלט שיוצג יהיה:

"click on document in capturing phase"
"click on <html> in capturing phase"
"click on <body> in capturing phase"
"click on #A in capturing phase"
"click on #B in capturing phase"
"click on #C in capturing phase"
"click on #C in bubbling phase"
"click on #B in bubbling phase"
"click on #A in bubbling phase"

אתם יכולים לשחק עם התכונה הזו באופן אינטראקטיבי בהדגמה הפעילה שבהמשך. לוחצים על הרכיב #C בדמו בזמן אמת ומתבוננים בפלט של המסוף.

עוד אחת, רק בשביל הכיף. מה קורה אם קוראים ל-stopPropagation() בשלב היעד של #C? חשוב לזכור ש'שלב היעד' הוא השם שניתן לתקופה שבה האירוע ב היעד. הפלט שיוצג יהיה:

"click on document in capturing phase"
"click on <html> in capturing phase"
"click on <body> in capturing phase"
"click on #A in capturing phase"
"click on #B in capturing phase"
"click on #C in capturing phase"

שימו לב שהגורם המטפל באירועים של #C שבו מתועד האירוע 'קליק על #C בשלב הצילום' עדיין מופעל, אבל הגורם המטפל באירועים שבו מתועד האירוע 'קליק על #C בשלב ההעברה (bubbling)' לא מופעל. זה הגיוני לחלוטין. התקשרנו אל stopPropagation() מ הקוד הקודם, כך שזו הנקודה שבה ההפצה של האירוע תיפסק.

אתם יכולים לשחק עם התכונה הזו באופן אינטראקטיבי בהדגמה הפעילה שבהמשך. לוחצים על הרכיב #C בדמו בזמן אמת ומתבוננים בפלט של המסוף.

מומלץ לנסות את כל הדגמות האלה בשידור חי. נסו ללחוץ רק על האלמנט #A או רק על האלמנט body. נסו לחזות מה יקרה ואז בדקו אם צדקתם. בשלב הזה, אמורה להיות לכם אפשרות לחזות את התוצאות בצורה די מדויקת.

event.stopImmediatePropagation()

מהי השיטה המוזרה הזו, שלא נעשה בה שימוש לעיתים קרובות? היא דומה ל-stopPropagation, אבל במקום למנוע מאירוע לעבור לצאצאים (תיעוד) או לאבות (העברה דרך שכבות), השיטה הזו חלה רק כשיש יותר ממנגנון טיפול אחד באירועים שמקושר לרכיב יחיד. מכיוון ש-addEventListener() תומך בסגנון אירועים של multicast, אפשר לקשר בורר אירועים לרכיב יחיד יותר מפעם אחת. במקרה כזה (ברוב הדפדפנים), מנהלי האירועים מופעל בסדר שבו הם מחוברים. קריאה ל-stopImmediatePropagation() מונעת הפעלה של מנהלים נוספים. דוגמה:

<html>
 
<body>
   
<div id="A">I am the #A element</div>
 
</body>
</html>
document.getElementById('A').addEventListener(
 
'click',
 
function (e) {
    console
.log('When #A is clicked, I shall run first!');
 
},
 
false,
);

document
.getElementById('A').addEventListener(
 
'click',
 
function (e) {
    console
.log('When #A is clicked, I shall run second!');
    e
.stopImmediatePropagation();
 
},
 
false,
);

document
.getElementById('A').addEventListener(
 
'click',
 
function (e) {
    console
.log('When #A is clicked, I would have run third, if not for stopImmediatePropagation');
 
},
 
false,
);

הדוגמה שלמעלה תוביל לפלט הבא במסוף:

"When #A is clicked, I shall run first!"
"When #A is clicked, I shall run second!"

שימו לב שבקר האירועים השלישי אף פעם לא פועל כי בקר האירועים השני קורא ל-e.stopImmediatePropagation(). אם היינו קוראים במקום זאת ל-e.stopPropagation(), הטיפול השלישי עדיין היה פועל.

event.preventDefault()

אם stopPropagation() מונע מאירוע לעבור "למטה" (תיעוד) או "למעלה" (העברה ברמה גבוהה יותר), מה preventDefault() עושה? נשמע שזה עושה משהו דומה. האם כן?

לא ממש. לעתים קרובות מתבלבלים בין השניים, אבל למעשה אין להם הרבה קשר זה לזה. כשרואים את הסמל preventDefault(), מוסיפים בראש את המילה 'פעולה'. המשמעות היא 'מניעת הפעולה שמוגדרת כברירת מחדל'.

ומה הפעולה שמוגדרת כברירת מחדל? לצערנו, התשובה לא ברורה כל כך, כי היא תלויה מאוד בשילוב של הרכיב והאירוע הרלוונטיים. ומה שמבלבל עוד יותר הוא שלפעמים אין פעולת ברירת מחדל בכלל.

נתחיל בדוגמה פשוטה מאוד שקל להבין. מה אתם מצפים שיקרה כשאתם לוחצים על קישור בדף אינטרנט? ברור שציפיתם שהדפדפן ינתב לכתובת ה-URL שצוינה בקישור הזה. במקרה כזה, האלמנט הוא תג עוגן והאירוע הוא אירוע קליק. לשילוב הזה (<a> + click) יש 'פעולת ברירת מחדל' של ניווט אל הערך של href של הקישור. מה קורה אם רוצים למנוע מהדפדפן לבצע את פעולת ברירת המחדל הזו? כלומר, נניח שרוצים למנוע מהדפדפן לנווט לכתובת ה-URL שצוינה במאפיין href של האלמנט <a>. זה מה ש-preventDefault() תעשה בשבילכם. עיין בדוגמה זו:

<a id="avett" href="https://www.theavettbrothers.com/welcome">The Avett Brothers</a>
document.getElementById('avett').addEventListener(
 
'click',
 
function (e) {
    e
.preventDefault();
    console
.log('Maybe we should just play some of their music right here instead?');
 
},
 
false,
);

אתם יכולים לשחק עם התכונה הזו באופן אינטראקטיבי בהדגמה הפעילה שבהמשך. לוחצים על הקישור The Avett Brothers ומתבוננים בפלט של המסוף (וגם בעובדה שלא מועברים לאתר של The Avett Brothers).

בדרך כלל, לחיצה על הקישור 'The Avett Brothers' תוביל לדפדוף אל www.theavettbrothers.com. במקרה הזה, חיברנו רכיב לטיפול באירועי קליקים לרכיב <a> והצבענו על כך שצריך למנוע את פעולת ברירת המחדל. לכן, כשמשתמש לוחץ על הקישור הזה, הוא לא מועבר לאף מקום. במקום זאת, ביומן של המסוף מתועדת ההודעה "Maybe we should just play some of their music right here instead?"

אילו שילובים אחרים של רכיבים/אירועים מאפשרים למנוע את פעולת ברירת המחדל? אי אפשר להכיל את כולן ברשימה, ולפעמים צריך פשוט להתנסות כדי לראות. אבל בקצרה, הנה כמה מהן:

  • רכיב <form> + אירוע 'שליחה': preventDefault() בשילוב הזה ימנע שליחת טופס. האפשרות הזו שימושית אם רוצים לבצע אימות, ואם משהו נכשל, אפשר לבצע קריאה מותנית ל-preventDefault כדי למנוע שליחת הטופס.

  • אלמנט <a> + אירוע 'קליק': preventDefault() בשילוב הזה מונע מהדפדפן לנווט לכתובת ה-URL שצוינה במאפיין href של האלמנט <a>.

  • document + אירוע 'גלגלת העכבר': preventDefault() בשילוב הזה, גלילה בדף באמצעות גלגלת העכבר לא תתבצע (אבל גלילה באמצעות המקלדת עדיין תעבוד).
    ↜ לשם כך צריך להפעיל את addEventListener() עם { passive: false }.

  • document + אירוע 'keydown': preventDefault() השילוב הזה קטלני. היא הופכת את הדף ללא שימושי במידה רבה, ומונע גלילה במקלדת, מעבר בין כרטיסיות והדגשה במקלדת.

  • document + אירוע 'mousedown': preventDefault() בשילוב הזה ימנע הדגשת טקסט באמצעות העכבר וכל פעולת ברירת מחדל אחרת שאפשר להפעיל באמצעות לחיצה על העכבר.

  • רכיב <input> + אירוע 'keypress': preventDefault() בשילוב הזה מונעים מאותיות שהמשתמש הקליד להגיע לרכיב הקלט (אבל אל תעשו זאת, כי יש לכך סיבה תקפה רק לעתים נדירות, אם בכלל).

  • document + אירוע 'contextmenu': preventDefault() השילוב הזה מונע את הצגת תפריט ההקשר המקורי של הדפדפן כשהמשתמש לוחץ לחיצה ימנית או לוחץ לחיצה ארוכה (או בכל דרך אחרת שבה תפריט ההקשר עשוי להופיע).

זו רשימה חלקית בלבד, אבל היא אמורה לתת לכם מושג טוב איך אפשר להשתמש ב-preventDefault().

מתיחה משעשעת?

מה קורה אם מבצעים את הפעולות stopPropagation() וגם preventDefault() בשלב הצילום, החל מהמסמך? ומה שקורה אחר כך הוא מצחיק מאוד. קטע הקוד הבא יהפוך כל דף אינטרנט ליותר או פחות חסר תועלת:

function preventEverything(e) {
  e
.preventDefault();
  e
.stopPropagation();
  e
.stopImmediatePropagation();
}

document
.addEventListener('click', preventEverything, true);
document
.addEventListener('keydown', preventEverything, true);
document
.addEventListener('mousedown', preventEverything, true);
document
.addEventListener('contextmenu', preventEverything, true);
document
.addEventListener('mousewheel', preventEverything, { capture: true, passive: false });

לא ברור לי למה בכלל תרצו לעשות את זה (חוץ מאשר כדי להצחיק מישהו), אבל כדאי לחשוב על מה שקורה כאן ולהבין למה זה יוצר את המצב הזה.

כל האירועים מתחילים ב-window, ולכן בקטע הקוד הזה אנחנו עוצרים את כל האירועים מסוג click,‏ keydown,‏ mousedown,‏ contextmenu ו-mousewheel, כך שהם לא יגיעו אף פעם לאלמנטים שעשויים להאזין להם. אנחנו גם קוראים ל-stopImmediatePropagation כדי שכל מנהלים שמחוברים למסמך אחרי זה ייחסמו גם הם.

חשוב לזכור ש-stopPropagation() ו-stopImmediatePropagation() הם לא (לפחות לא בעיקר) הגורמים שהופכים את הדף ללא שימושי. הן פשוט מונעות מאירועים להגיע למקום שאליו הם היו מגיעים אחרת.

אבל אנחנו קוראים גם לפונקציה preventDefault(), שמונעת את הפעולה שמוגדרת כברירת מחדל. לכן, כל פעולות ברירת המחדל (כמו גלילה באמצעות גלגל העכבר, גלילה במקלדת או הדגשה או מעבר בין כרטיסיות, לחיצה על קישור, הצגת תפריט הקשר וכו') נמנעות, כך שהדף נשאר במצב לא שימושי למדי.

הדגמות בזמן אמת

כדי לראות שוב את כל הדוגמאות מהמאמר הזה במקום אחד, אפשר לצפות בדמו המוטמע שבהמשך.

תודות

התמונה הראשית (Hero) של Tom Wilson ב-Unsplash.