preventDefault
وstopPropagation
: متى نستخدم كل طريقة وماذا تفعل كل طريقة بالضبط؟
Event.stopPropagation() وEvent.preventDefault()
غالبًا ما تكون معالجة أحداث JavaScript بسيطة. ويصدق ذلك بشكل خاص عند التعامل مع بنية HTML بسيطة (مسطّحة نسبيًا). تصبح الأمور أكثر تعقيدًا عندما تنتقل الأحداث (أو تنتشر) عبر تسلسل هرمي من العناصر. وعادةً ما يلجأ المطوّرون إلى استخدام stopPropagation()
و/أو preventDefault()
لحلّ المشاكل التي يواجهونها. إذا سبق لك أن فكّرت "سأجرّب preventDefault()
وإذا لم ينجح ذلك سأجرّب stopPropagation()
وإذا لم ينجح ذلك سأجرّب كليهما"، فهذه المقالة مناسبة لك. سأشرح لك بالتفصيل وظيفة كل طريقة، ومتى يجب استخدامها، وسأقدّم لك مجموعة متنوعة من الأمثلة العملية لتستكشفها. هدفي هو إنهاء حالة الارتباك لديك نهائيًا.
قبل أن نتوسّع في الحديث عن هذا الموضوع، من المهم أن نتناول بإيجاز نوعَي معالجة الأحداث الممكنَين في JavaScript (في جميع المتصفّحات الحديثة، لأنّ الإصدارات السابقة للإصدار 9 من Internet Explorer لم تكن تتيح إمكانية تسجيل الأحداث على الإطلاق).
أنماط معالجة الأحداث (الالتقاط والفقاعات)
تتيح جميع المتصفحات الحديثة إمكانية تسجيل الأحداث، ولكن نادرًا ما يستخدمها المطوّرون.
من المثير للاهتمام أنّ هذا النوع من الأحداث كان النوع الوحيد الذي كان متوافقًا مع Netscape في الأصل. لم يكن متصفّح Microsoft Internet Explorer، المنافس الأكبر لمتصفّح Netscape، يتيح تسجيل الأحداث على الإطلاق، بل كان يتيح فقط نمطًا آخر من الأحداث يُعرف باسم "تتابع الأحداث". عندما تم تأسيس W3C، وجدوا مزايا في كلا أسلوبي معالجة الأحداث وأعلنوا أنّه على المتصفحات دعم كلا الأسلوبين من خلال معلَمة ثالثة لطريقة addEventListener
. في الأصل، كانت هذه المَعلمة مجرد قيمة منطقية، ولكن جميع المتصفحات الحديثة تتيح استخدام عنصر options
كالمَعلمة الثالثة، ويمكنك استخدامها لتحديد (من بين أمور أخرى) ما إذا كنت تريد استخدام ميزة "التقاط الأحداث" أم لا:
someElement.addEventListener('click', myClickHandler, { capture: true | false });
يُرجى العِلم أنّ العنصر options
اختياري، وكذلك السمة capture
. في حال حذف أيّ منهما، ستكون القيمة التلقائية لـ capture
هي false
، ما يعني أنّه سيتم استخدام ميزة "تتابع الأحداث".
تسجيل الأحداث
ماذا يعني أن تكون أداة معالجة الأحداث "تستمع في مرحلة الالتقاط"؟ لفهم ذلك، علينا معرفة كيفية إنشاء الأحداث وكيفية انتقالها. ينطبق ما يلي على جميع الأحداث، حتى إذا لم تستفِد منها كمطوّر أو لم تهتم بها أو لم تفكّر فيها.
تبدأ جميع الأحداث في النافذة وتمرّ أولاً بمرحلة الالتقاط. وهذا يعني أنّه عند إرسال حدث، يبدأ الحدث في النافذة ويتنقّل "للأسفل" نحو العنصر المستهدف أولاً. يحدث ذلك حتى إذا كنت تستمع فقط في مرحلة الفقاعات. إليك مثالاً على الترميز و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
في مرحلة الالتقاط؟". الإجابة هذه المرة هي "نعم". تُعرف هذه الفترة الزمنية القصيرة التي يكون فيها الحدث في الهدف باسم "مرحلة الاستهداف". في هذه المرحلة، سيتم تشغيل معالج الأحداث، وسيطبع المتصفّح "تم النقر على #C" في وحدة التحكّم، وبعد ذلك سننتهي، أليس كذلك؟
إجابة غير صحيحة لم ننتهِ بعد. تستمر العملية، ولكنها تنتقل الآن إلى مرحلة الفقاعة.
تفعيل ميزة "تداخل الأحداث"
سيسألك المتصفّح:
"هل هناك أي شيء يستمع إلى حدث النقر على #C
في مرحلة الفقاعات؟" يُرجى الانتباه جيدًا إلى ما يلي.
من الممكن تمامًا الاستماع إلى النقرات (أو أي نوع من الأحداث) في كلتا مرحلتي الالتقاط والتوسيع. وإذا كنت قد ربطت معالجات الأحداث في كلتا المرحلتين (على سبيل المثال، من خلال استدعاء
.addEventListener()
مرتين، مرة باستخدام capture = true
ومرة باستخدام capture = false
)، نعم،
سيتم تشغيل كلتا معالجتَي الأحداث للعنصر نفسه. من المهم أيضًا ملاحظة أنّها يتم تشغيلها في مراحل مختلفة (واحدة في مرحلة الالتقاط والأخرى في مرحلة الفقاعة).
بعد ذلك، سيتم نشر الحدث (يُشار إليه عادةً باسم "الفقاعة" لأنّه يبدو وكأنّ الحدث ينتقل "إلى أعلى" شجرة DOM) إلى العنصر الأصل، #B
، وسيطرح المتصفّح السؤال التالي: "هل هناك أي شيء يستمع إلى أحداث النقرات على #B
في مرحلة الفقاعة؟". في مثالنا، لا يوجد أي شيء، لذا لن يتم تشغيل أي معالجات.
بعد ذلك، سيتم تمرير الحدث إلى #A
وسيطرح المتصفّح السؤال التالي: "هل هناك أي شيء يستمع إلى أحداث النقر على #A
في مرحلة الفقاعة؟".
بعد ذلك، سيتمّ تمرير الحدث إلى <body>
: "هل هناك أيّ شيء يستمع إلى أحداث النقر على العنصر <body>
في مرحلة الفقاعات؟"
بعد ذلك، العنصر <html>
: "هل هناك أي شيء يستمع إلى أحداث النقر على العنصر <html>
في مرحلة الانتشار؟
بعد ذلك، document
: "هل هناك أي شيء يستمع إلى أحداث النقر على document
في مرحلة الفقاعات؟"
أخيرًا، 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
)، سيتم تشغيل كل معالجات الأحداث هذه. بعد إضافة بعض أنماط CSS لتوضيح العناصر، إليك عنصر #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"
"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"
event.stopPropagation()
بعد أن فهمنا مصدر الأحداث وكيفية انتقالها (أي انتشارها) عبر نموذج المستند (DOM) في كل من مرحلة الالتقاط ومرحلة الفقاعة، يمكننا الآن التركيز على event.stopPropagation()
.
يمكن استدعاء الطريقة stopPropagation()
في (معظم) أحداث DOM الأصلية. أقول "معظم" لأنّ هناك بعض الأحداث التي لن يؤدي استدعاء هذه الطريقة إلى أي شيء (لأنّ الحدث لا ينتشر في البداية). تندرج أحداث مثل focus
وblur
وload
وscroll
وبعض الأحداث الأخرى ضمن هذه الفئة. يمكنك الاتصال بـ stopPropagation()
ولكن لن يحدث أي شيء مثير للاهتمام، لأنّ هذه الأحداث لا تنتشر.
ولكن ما هي وظيفة stopPropagation
؟
وهو يفعل، إلى حد كبير، ما يقوله. عند استدعاء هذا الإجراء، سيتوقف الحدث من تلك النقطة عن الانتشار إلى أي عناصر أخرى كان من المفترض أن ينتقل إليها. وينطبق ذلك على كلتا الجهتين
(التقاط الحدث وتصعيده). لذلك، إذا استدعيت stopPropagation()
في أي مكان في مرحلة الالتقاط، لن يصل الحدث أبدًا إلى مرحلة الاستهداف أو مرحلة الفقاعات. إذا استدعيت الدالة في مرحلة الفقاعات، ستكون قد مرّت بمرحلة الالتقاط، ولكنها ستتوقف عن "الارتفاع" من النقطة التي استدعيتها فيها.
بالعودة إلى ترميز المثال نفسه، ماذا سيحدث إذا استدعينا 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"
ماذا عن إيقاف الانتشار عند #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"
إليك واحدة أخرى، فقط للتسلية. ماذا يحدث إذا استدعينا 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 في مرحلة الفقاعة" لا يتم تنفيذه. من المفترض أن يكون ذلك منطقيًا تمامًا. لقد اتصلنا بـ stopPropagation()
من العنوان السابق، وبالتالي، ستتوقف عملية نشر الحدث عند هذه النقطة.
أشجّعك على تجربة أيّ من هذه العروض التوضيحية المباشرة. جرِّب النقر على العنصر #A
فقط أو على العنصر body
فقط. حاوِل توقّع ما سيحدث ثم راقِب ما إذا كان توقّعك صحيحًا. في هذه المرحلة، من المفترض أن تتمكّن من التنبؤ بدقة كبيرة.
event.stopImmediatePropagation()
ما هي هذه الطريقة الغريبة وغير الشائعة؟ وهي تشبه stopPropagation
، ولكن بدلاً من منع حدث من الانتقال إلى العناصر التابعة (الالتقاط) أو العناصر الرئيسية (التصاعد)، لا تنطبق هذه الطريقة إلا عندما يكون لديك أكثر من معالج أحداث واحد مرتبط بعنصر واحد. بما أنّ
addEventListener()
يتيح استخدام نمط البث المتعدد للأحداث، من الممكن تمامًا ربط معالج أحداث بعنصر واحد أكثر من مرة. عند حدوث ذلك، (في معظم المتصفحات)، يتم تنفيذ معالجات الأحداث بالترتيب الذي تم ربطها به. يؤدي استدعاء 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 إلى الانتقال إلى
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
+ "mousewheel": يمنع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()
، الذي يمنع الإجراء التلقائي. وبالتالي، يتم منع أي إجراءات تلقائية (مثل التمرير باستخدام عجلة الماوس أو لوحة المفاتيح أو التمييز أو الانتقال باستخدام المفتاح Tab أو النقر على الروابط أو عرض قائمة السياق وما إلى ذلك)، ما يجعل الصفحة في حالة غير مفيدة إلى حدّ كبير.
الإقرارات
الصورة الرئيسية من توم ويلسون على Unsplash