Event Delegation: مدیریت هوشمند رویدادها با یک شنونده

تصور کنید یک لیست با صدها آیتم دارید و می‌خواهید با کلیک روی هر آیتم، یک عملیات مشخص اجرا شود. راه ساده اما ناکارآمد این است که برای تک‌تک آیتم‌ها یک addEventListener جداگانه ثبت کنید. الگوی Event Delegation راه‌حلی بسیار بهتر ارائه می‌دهد: به‌جای گوش‌دادن به هر عنصر به‌صورت جداگانه، فقط روی عنصر والد مشترک یک شنونده ثبت می‌کنید و از رفتار Bubbling که در درس قبل بررسی شد، برای شناسایی عنصر واقعی که رویداد از آن آغاز شده استفاده می‌کنید.

چرا Event Delegation؟

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

شناسایی عنصر واقعی هدف با event.target

وقتی شنونده روی عنصر والد ثبت می‌شود، ویژگی event.target دقیقاً همان عنصری را برمی‌گرداند که کاربر روی آن کلیک کرده، نه لزوماً عنصری که شنونده روی آن نصب شده. این تفاوت بین event.target و event.currentTarget اساس کار Delegation است:

ویژگی event.target event.currentTarget
مقدار عنصری که رویداد از آن شروع شده عنصری که شنونده روی آن ثبت شده
تغییر در طول انتشار رویداد ثابت می‌ماند در هر مرحله به عنصر جاری تغییر می‌کند
کاربرد در Delegation شناسایی عنصر واقعی کلیک‌شده معمولاً کاربرد مستقیمی در Delegation ندارد

پیاده‌سازی عملی یک لیست پویا

در مثال زیر، به‌جای ثبت شنونده روی هر li، یک شنونده روی عنصر ul ثبت شده و با استفاده از متد closest، نزدیک‌ترین li نسبت به عنصر کلیک‌شده پیدا می‌شود:

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

استفاده از closest("li") به‌جای بررسی مستقیم event.target اهمیت زیادی دارد، چون اگر داخل هر li عنصر دیگری مثل یک آیکن یا span وجود داشته باشد، کلیک روی آن عنصر داخلی رخ می‌دهد و event.target به آن اشاره می‌کند، نه به خود li. متد closest با بالا رفتن از درخت DOM، نزدیک‌ترین اجداد منطبق با سلکتور را پیدا می‌کند و این مشکل را برطرف می‌سازد.

محدودیت‌های Event Delegation

این الگو برای همه‌ی رویدادها مناسب نیست. رویدادهایی مثل focus و blur به‌صورت پیش‌فرض Bubble نمی‌شوند و باید یا از نسخه‌ی جایگزین آن‌ها یعنی focusin و focusout استفاده کرد، یا شنونده را مستقیماً روی عنصر مورد نظر ثبت کرد. همچنین در درختان DOM بسیار عمیق، هر بار اجرای closest کمی هزینه‌ی پردازشی دارد که در حجم بالای رویداد قابل بررسی است.

نکات کلیدی

  • Event Delegation از رفتار Bubbling استفاده می‌کند تا با یک شنونده، رویدادهای چندین عنصر فرزند مدیریت شود.
  • event.target عنصر واقعی کلیک‌شده را نشان می‌دهد و event.currentTarget عنصر دارای شنونده را.
  • متد closest() برای پیدا کردن عنصر والد منطبق با یک سلکتور، حتی وقتی کلیک روی فرزندان داخلی رخ داده، ضروری است.
  • این الگو برای عناصر پویا که بعداً به صفحه اضافه می‌شوند نیز بدون نیاز به ثبت شنونده‌ی جدید کار می‌کند.
  • رویدادهایی مثل focus و blur Bubble نمی‌شوند و برای Delegation باید از focusin/focusout استفاده کرد.

جمع‌بندی

Event Delegation یکی از کاربردی‌ترین الگوهای مبتنی بر Bubbling در جاوااسکریپت است که با ثبت یک شنونده روی عنصر والد، مدیریت رویداد برای تعداد نامحدودی عنصر فرزند، از جمله عناصری که بعداً به‌صورت پویا اضافه می‌شوند، ممکن می‌شود. ترکیب event.target با متد closest() ابزار اصلی برای شناسایی دقیق عنصر هدف در این الگو است. با این حال، باید به رویدادهایی که Bubble نمی‌شوند توجه داشت و از جایگزین مناسب آن‌ها استفاده کرد.