غواصی عمیق رویداد جاوا اسکریپت

preventDefault و stopPropagation : چه زمانی باید از کدام روش استفاده کرد و دقیقاً چه کاری انجام می دهد.

مدیریت رویداد جاوا اسکریپت اغلب ساده است. این امر به ویژه هنگامی که با ساختار HTML ساده (نسبتاً مسطح) سروکار داریم، صادق است. وقتی رویدادها از طریق سلسله مراتبی از عناصر در حال حرکت (یا انتشار) هستند، همه چیز کمی بیشتر درگیر می شود. این معمولاً زمانی است که توسعه‌دهندگان برای حل مشکلاتی که با آن مواجه هستند به سراغ stopPropagation() و/یا preventDefault() می‌روند. اگر تا به حال با خود فکر کرده اید که "من فقط preventDefault() امتحان می کنم و اگر کار نکرد، stopPropagation() امتحان می کنم و اگر کار نکرد، هر دو را امتحان می کنم، پس این مقاله برای شما! من دقیقاً توضیح خواهم داد که هر روش چه کاری انجام می دهد، چه زمانی باید از کدام یک استفاده کرد، و نمونه های کاری متنوعی را برای شما ارائه می کنم تا آنها را بررسی کنید. هدف من این است که یک بار برای همیشه به سردرگمی شما پایان دهم.

با این حال، قبل از اینکه خیلی عمیق بشویم، مهم است که به طور مختصر به دو نوع مدیریت رویداد ممکن در جاوا اسکریپت بپردازیم (در همه مرورگرهای مدرن، اینترنت اکسپلورر قبل از نسخه 9 اصلاً از ضبط رویداد پشتیبانی نمی کرد).

سبک های مراسم (گرفتن و حباب زدن)

همه مرورگرهای مدرن از ضبط رویداد پشتیبانی می کنند، اما توسعه دهندگان به ندرت از آن استفاده می کنند. جالب اینجاست که این تنها شکل رویدادی بود که نت اسکیپ در ابتدا از آن پشتیبانی می کرد. بزرگترین رقیب نت اسکیپ، مایکروسافت اینترنت اکسپلورر، به هیچ وجه از ضبط رویداد پشتیبانی نمی کرد، بلکه تنها از سبک دیگری از رویدادها به نام حباب رویداد پشتیبانی می کرد. هنگامی که W3C تشکیل شد، آنها در هر دو سبک رویدادها شایستگی پیدا کردند و اعلام کردند که مرورگرها باید از هر دو، از طریق پارامتر سوم به روش addEventListener پشتیبانی کنند. در ابتدا، این پارامتر فقط یک بولی بود، اما همه مرورگرهای مدرن از یک شی options به عنوان پارامتر سوم پشتیبانی می‌کنند، که می‌توانید از آن برای تعیین (در میان موارد دیگر) استفاده کنید که آیا می‌خواهید از ثبت رویداد استفاده کنید یا نه:

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

توجه داشته باشید که شی options اختیاری است، همچنین ویژگی capture آن نیز اختیاری است. اگر یکی از آنها حذف شود، مقدار پیش‌فرض برای capture false است، به این معنی که از حباب رویداد استفاده می‌شود.

ثبت رویداد

اگر مدیریت رویداد شما "در مرحله ضبط گوش می دهد" به چه معناست؟ برای درک این موضوع، باید بدانیم که رویدادها چگونه منشا می گیرند و چگونه سفر می کنند. موارد زیر در مورد همه رویدادها صادق است، حتی اگر شما به عنوان توسعه دهنده، از آن استفاده نکنید، به آن اهمیت ندهید یا به آن فکر نکنید.

همه رویدادها از پنجره شروع می شوند و ابتدا مرحله ضبط را طی می کنند. این بدان معنی است که هنگامی که یک رویداد ارسال می شود، پنجره را شروع می کند و ابتدا به سمت عنصر هدف خود به سمت پایین حرکت می کند. این اتفاق می افتد حتی اگر شما فقط در مرحله حباب گوش می دهید. مثال زیر نشانه گذاری و جاوا اسکریپت را در نظر بگیرید:

<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 کلیک شد" را انجام می دهد و سپس کارمان تمام می شود، درست است؟ اشتباه! ما اصلاً تمام نشده ایم. این روند ادامه دارد، اما اکنون به مرحله حباب تغییر می کند.

حباب رویداد

مرورگر می پرسد:

"آیا چیزی برای یک رویداد کلیک روی #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"

شما می توانید به صورت تعاملی با این در دمو زنده زیر بازی کنید. روی عنصر #C کلیک کنید و خروجی کنسول را مشاهده کنید.

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"

شما می توانید به صورت تعاملی با این در دمو زنده زیر بازی کنید. روی عنصر #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 در دمو زنده کلیک کنید و خروجی کنسول را مشاهده کنید.

یکی دیگه فقط برای سرگرمی اگر در فاز هدف برای #C stopPropagation() فراخوانی کنیم، چه اتفاقی می‌افتد؟ به یاد بیاورید که "فاز هدف" نامی است که به دوره زمانی داده می شود که رویداد در هدف خود قرار دارد. به خروجی زیر منجر می شود:

"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 که در آن Log "در مرحله ثبت بر روی #C کلیک کنید" همچنان اجرا می شود، اما یکی که در آن Log می کنیم "در مرحله حباب بر روی #C کلیک کنید" اجرا نمی شود. این باید کاملاً منطقی باشد. ما stopPropagation() از اولی فراخوانی کردیم، بنابراین این نقطه ای است که در آن انتشار رویداد متوقف می شود.

شما می توانید به صورت تعاملی با این در دمو زنده زیر بازی کنید. روی عنصر #C در دمو زنده کلیک کنید و خروجی کنسول را مشاهده کنید.

در هر یک از این دموهای زنده، من شما را تشویق می کنم که در اطراف بازی کنید. فقط روی عنصر #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() مشاهده کردید، در سر خود کلمه "action" را اضافه کنید. به "جلوگیری از اقدام پیش فرض" فکر کنید.

و اقدام پیش فرضی که ممکن است بپرسید چیست؟ متأسفانه، پاسخ به آن کاملاً واضح نیست زیرا به شدت به ترکیب عنصر + رویداد مورد نظر وابسته است. و برای گیج شدن بیشتر، گاهی اوقات هیچ اقدام پیش فرضی وجود ندارد!

بیایید با یک مثال بسیار ساده برای درک شروع کنیم. وقتی روی یک لینک در یک صفحه وب کلیک می کنید، انتظار دارید چه اتفاقی بیفتد؟ بدیهی است که انتظار دارید مرورگر به 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 کلیک کنید و خروجی کنسول (و این واقعیت که شما به وب سایت Avett Brothers هدایت نشده اید) را مشاهده کنید.

به طور معمول، کلیک کردن بر روی پیوند با عنوان The Avett Brothers منجر به مرور به www.theavettbrothers.com می شود. با این حال، در این مورد، ما یک کنترل کننده رویداد کلیک را به عنصر <a> متصل کرده‌ایم و مشخص کرده‌ایم که از عملکرد پیش‌فرض باید جلوگیری شود. بنابراین، زمانی که کاربر روی این پیوند کلیک می‌کند، به هیچ‌جا هدایت نمی‌شود، و در عوض کنسول به سادگی وارد سیستم می‌شود: «شاید ما فقط برخی از موسیقی‌های او را همین‌جا پخش کنیم؟»

چه ترکیب‌های عنصر/رویداد دیگری به شما امکان می‌دهد از عملکرد پیش‌فرض جلوگیری کنید؟ من نمی توانم همه آنها را فهرست کنم، و گاهی اوقات برای دیدن باید فقط آزمایش کنید. اما به طور خلاصه به چند مورد اشاره می کنیم:

  • عنصر <form> + رویداد "submit": preventDefault() برای این ترکیب از ارسال فرم جلوگیری می کند. اگر می‌خواهید اعتبارسنجی را انجام دهید و در صورت عدم موفقیت، می‌توانید به طور مشروط با preventDefault تماس بگیرید تا فرم ارسال نشود.

  • عنصر <a> + رویداد "click": 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() در مرحله capturing، با شروع از سند، متوقف کنید؟ شوخ طبعی به وجود می آید! قطعه کد زیر هر صفحه وب را تقریباً کاملاً بی فایده نشان می دهد:

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() فراخوانی می کنیم، که به یاد می آورید از عمل پیش فرض جلوگیری می کند. بنابراین هر گونه عملکرد پیش فرض (مانند پیمایش چرخ ماوس، پیمایش صفحه کلید یا برجسته کردن یا تب، کلیک کردن روی پیوند، نمایش منوی زمینه، و غیره) همگی جلوگیری می شود، بنابراین صفحه در حالت نسبتاً بی فایده باقی می ماند.

دموهای زنده

برای بررسی مجدد همه نمونه‌های این مقاله در یک مکان، نسخه نمایشی تعبیه‌شده زیر را بررسی کنید.

قدردانی

تصویر قهرمان توسط تام ویلسون در Unsplash .