مدل رویداد در جاوااسکریپت و شیء Event

مقدمه

در فصل‌های قبل، یاد گرفتیم چگونه عناصر DOM را انتخاب کنیم، بسازیم، تغییر دهیم و حذف کنیم؛ اما تمام این عملیات، بدون واکنش به کاربر، مستقیماً و یک‌بار در کد اجرا می‌شدند. یک صفحه‌ی وب واقعی باید به کارهایی که کاربر انجام می‌دهد -کلیک کردن، تایپ کردن، اسکرول کردن- واکنش نشان دهد. این فصل به مدل رویداد (Event Model) جاوااسکریپت می‌پردازد؛ مکانیزمی که این واکنش‌پذیری را ممکن می‌سازد.

Event چیست؟

یک Event (رویداد) به هر اتفاقی گفته می‌شود که مرورگر آن را تشخیص می‌دهد و می‌تواند به آن اطلاع‌رسانی کند؛ کلیک روی یک دکمه، فشردن یک کلید، بارگذاری کامل صفحه، یا تغییر مقدار یک ورودی، همگی نمونه‌هایی از Event هستند. جاوااسکریپت به ما اجازه می‌دهد کدی بنویسیم که دقیقاً زمانی که یکی از این اتفاقات رخ می‌دهد، اجرا شود.

addEventListener — ثبت یک واکنش به رویداد

رایج‌ترین و توصیه‌شده‌ترین روش برای واکنش به یک رویداد، متد addEventListener است:

ثبت یک Event Listener ساده برای کلیک
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

addEventListener روی عنصر موردنظر فراخوانی می‌شود و دو آرگومان اصلی می‌گیرد: نام رویداد به‌صورت رشته (در اینجا "click"، بدون پیشوند on)، و یک تابع Callback که باید هر بار آن رویداد رخ دهد، اجرا شود. این تابع Callback دقیقاً همان مفهومی است که در فصل توابع پیشرفته، هنگام بررسی Callback ناهمزمان، دیدیم؛ کد داخل آن، نه بلافاصله، بلکه فقط در لحظه‌ای که کاربر واقعاً کلیک کند، اجرا می‌شود.

Event Object — اطلاعات کامل درباره‌ی رویداد رخ‌داده

وقتی یک رویداد رخ می‌دهد، مرورگر به‌طور خودکار یک شیء حاوی جزئیات کامل آن رویداد می‌سازد و آن را به‌عنوان اولین آرگومان، به تابع Callback پاس می‌دهد. به این شیء، Event Object می‌گویند:

دریافت و بررسی Event Object
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل رفتار جاوااسکریپت

پارامتر event در اینجا نامی دلخواه است (می‌توانستیم آن را e یا هر نام دیگری بگذاریم)؛ نکته‌ی مهم این است که مرورگر همیشه، به‌عنوان اولین آرگومان تابع Callback، این شیء را در اختیار قرار می‌دهد. event.type نام دقیق رویداد رخ‌داده را برمی‌گرداند (در اینجا "click")، و event.target دقیقاً همان عنصر DOM ای است که رویداد روی آن اتفاق افتاده. این دو Property از پرکاربردترین اطلاعاتی هستند که تقریباً در هر Event Object، صرف‌نظر از نوع رویداد، در دسترس‌اند.

تفاوت target و currentTarget

Event Object همچنین Property دیگری به نام currentTarget دارد که ممکن است در نگاه اول با target یکسان به‌نظر برسد:

outerDiv.addEventListener("click", function (event) {
  console.log(event.target);         // دقیقاً همان عنصری که کلیک روی آن رخ داد
  console.log(event.currentTarget);  // همیشه همان عنصری که Listener روی آن ثبت شده (outerDiv)
});

اگر outerDiv شامل عناصر دیگری باشد و کاربر روی یکی از آن فرزندان کلیک کند، event.target همان فرزند دقیق کلیک‌شده خواهد بود، درحالی‌که event.currentTarget همیشه outerDiv باقی می‌ماند؛ چون Listener روی همان عنصر ثبت شده است. دلیل دقیق این رفتار، به مکانیزمی به نام Bubbling برمی‌گردد که در درس بعدی همین فصل به‌طور کامل بررسی می‌شود.

حذف یک Event Listener با removeEventListener

اگر بخواهیم یک Listener را در آینده حذف کنیم، باید تابع Callback را از قبل در یک متغیر مستقل (نه به‌صورت Anonymous) تعریف کرده باشیم، چون removeEventListener نیاز به همان ارجاع دقیق تابع دارد:

حذف یک Event Listener
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این مثال، چون removeEventListener بلافاصله بعد از addEventListener و با دقیقاً همان ارجاع تابع (handleClick) فراخوانی شده، Listener اصلاً هرگز فرصت اجرا شدن پیدا نمی‌کند. اگر به‌جای پاس دادن handleClick، دو تابع Anonymous جداگانه (حتی با محتوای کاملاً یکسان) به addEventListener و removeEventListener پاس داده شوند، حذف موفق نخواهد بود؛ چون از دید جاوااسکریپت، این دو تابع دو مقدار کاملاً متفاوت در حافظه هستند، حتی اگر رفتار یکسانی داشته باشند.

روش‌های قدیمی‌تر: on-Event و HTML Attribute

پیش از رواج addEventListener، دو روش قدیمی‌تر رایج بودند که هنوز هم در کدهای قدیمی دیده می‌شوند:

// روش قدیمی اول: نسبت‌دهی مستقیم به Property
button.onclick = function () {
  console.log("کلیک شد");
};

مشکل اصلی این روش این است که هر عنصر فقط می‌تواند یک onclick واحد داشته باشد؛ اگر دوباره چیزی به button.onclick نسبت دهیم، مقدار قبلی کاملاً بازنویسی و از بین می‌رود. اما addEventListener این محدودیت را ندارد؛ می‌توان چندین Listener مستقل برای یک رویداد یکسان، روی یک عنصر یکسان ثبت کرد و همگی به‌ترتیب اجرا خواهند شد.

جدول جمع‌بندی

مفهومتوضیح
addEventListener(type, fn)ثبت یک واکنش به رویداد؛ امکان چند Listener مستقل روی یک عنصر
Event Objectشیئی حاوی جزئیات رویداد، به‌عنوان آرگومان اول به Callback پاس داده می‌شود
event.targetعنصر دقیقی که رویداد مستقیماً روی آن رخ داده
event.currentTargetعنصری که Listener روی آن ثبت شده
removeEventListenerحذف Listener؛ نیازمند ارجاع دقیق همان تابع ثبت‌شده

جمع‌بندی

مدل رویداد در جاوااسکریپت، امکان واکنش به اتفاقاتی مثل کلیک یا تایپ کردن را از طریق addEventListener فراهم می‌کند؛ روشی که برخلاف نسخه‌های قدیمی‌تر مثل onclick، اجازه‌ی ثبت چند Listener مستقل روی یک عنصر را می‌دهد. هر بار که رویدادی رخ می‌دهد، مرورگر یک Event Object می‌سازد که جزئیاتی مثل type، target و currentTarget را در اختیار Callback قرار می‌دهد.