بررسی مفهوم bubbling و نحوه عملکرد event ها در جاوا اسکریپت

بیان ساده مفهوم bubbling در جاوا اسکریپت
بیان ساده مفهوم bubbling در جاوا اسکریپت

یکی از مفاهیم پایه ای جاوا اسکریپت در مورد نحوه عملکرد event ها bubbling هست که در این پست به زبان ساده اون رو شرح داده ام.

در واقع event bubbling به شرایطی اشاره دارد که یک event، روی یک المان nested در المان دیگر اتفاق افتاده است و هر دوی آنها روی همان event دارای event handler هستند. خوب حالا event bubbling مشخص میکند که کدام handler اول صدا زده شود.


نحوه ی الویت بندی صدا زده شدن event handlers
نحوه ی الویت بندی صدا زده شدن event handlers


برای مشخص شدن موضوع بیایید به یک مثال ساده نگاه کنیم:

<ul>
    <li><a href="..."><img src="..." alt=""></a>
    <li><a href="..."><img src="..." alt=""></a>
        ...
    <li><a href="..."><img src="..." alt=""></a>
</ul>

در اینجا فرض کنید یک event از نوع click روی img tag که داخلی ترین tag در هر سطح هست داشته باشیم.

پس img رو طبق تعاریف جاوا اسکریپت event target می نامیم و اگر بخواهیم تمامی والدین اون رو تا window نام ببریم خواهیم داشت:

IMG, A, LI, UL, BODY, HTML, document, window

خوب به ترتیب از سمت چپ به راست مسیر پیمایش event ما در node های صفحه می باشد که اصطلاحا propagate کردن گفته می شود و اگر هر کدوم از این node ها دارای event یکسان با event اتفاق افتاده روی IMG باشند، صدا زده می شوند.

برای وضوح بیشتر فرض کنید:

۱. شما روی تگ IMG یک event handler دارید که اگر کلیک شد عکس را به صورت بزرگ در modal نمایش دهید

۲. هم چنین یک event handler هم روی li دارید که اگر کلیک شد اطلاعات بیشتری از این ردیف رو به کاربر نشون بدید.

خوب طبق تعریف ما اگر برروی IMG کیلیک کنیم هر دوی این event handler ها صدا زده میشوند!

چطوری از صدا زده شدن handler های دیگه جلوگیری کنیم؟

فرض کنیم در مثال بالا نمی خواهیم با کلیک روی IMG به صورت پیش فرض جاوا اسکریپت بقیه handler ها را صدا بزند.

برای این کار کافی هست در تابعی که به عنوان eventListener روی IMG اضافه کردیم از propagate شدن بیشتر جلوگیری کنیم:

function imgOnClickEventListener(event){
    if(event.bubbling){
        event.stopPropagation();
    }
    console.log('event fired!!!');
}
imgSelected.addEventListener('click', imgOnClickEventListener);

در کد بالا یک تابع به اسم imgOnClickEventListener داریم و اون رو به عنوان listerner به img اضافه کردیمش و هنگام کلیک روی img جاوا اسکریپت این تابع را صدا میزند و object از نوع event اتفاق افتاده را به عنوان ورودی به اون پاس میده.

مقدار ورودی imgOnClickEventListener دارای یک property به اسم bubbling هست که یک مقدار boolean ما برمی گرداند و اگر true باشد بعد از اجرای این تابع باید سایر listener ها را نیز صدا بزند که خوب در این صورت اگر تابع stopPropagation رو صدا کینم از این کار منصرف میشه و سایر listener ها اجرا نمی شوند.

چگونه الویت اجرای listener ها را جا به جا کنیم؟

در مثال بالا فرض کنید می خواهیم نه تنها هر دوی listener هایی که روی img و li تعریف کردیم اجرا شوند بلکه با کلیک روی img ابتدا li اجرا شود و بعد img.

خوب برای این کار باید در زمان اضافه کردن listener برای li ورودی سوم addEventListener رو true بدهیم تا مشخص کنیم که این listener در روند اجرای bubbling الویت بیشتری دارد:

liSelected.addEventListener('click', liOnClickEventListener, true);

خوب الان اتفاقی که میخواهیم می افتد و با کلیک روی img اول تابع liOnClickEventListener صدا زده میشود و بعد imgOnClickEventListener.

بررسی دقیق تر نحوه ی پیمایش event ها در جاوا اسکریپت

خوب تا اینجا در حالت ساده گفته شد که چطوی event listener ها صدا زده می شوند و چطوی میتونیم الویت آنها را جابه جا کنیم یا از صدا زدن سایر listener ها مطلع شویم و یا از آن جلو گیری کنیم.

اما ببینیم این کار ها در جاوا اسکریپت چگونه مدیریت میشوند، برای این مورد باید ۳ تا تعریف زیر رو بدونیم:

نحوه ی اجرای فاز های صدا زدن event listener ها
نحوه ی اجرای فاز های صدا زدن event listener ها

Capture Phase

Target Phase

Bubbling Phase

خوب بیایید همون مسیر مثال قبلی رو در نظر بگیریم:

IMG, A, LI, UL, BODY, HTML, document, window

۱. اولین فازی که اجرا می شود Capture هست، در این فاز از بالا به پایین node ها پیمایش می شوند اون هایی که الویت بالا تری در خواست داده باشند شناسایی و اجرا می شوند.

یعنی همون کاری که ما با liOnClickEventListener انجام دادیم و با true دادن پارامتر سوم تابع addEventListener الویت بالاتری رو درخواست کردیم.

پس تمام listener ها از این نوع رو در این فاز اجرا میکند و فراموش نکنیم در این فاز top down می باشد یعنی از window که بالاترین سطح جاوا اسکریپت هست به پاییین پیمایش می شود تا به event target node برسه.

۲. فاز دوم Target هست یعنی دقیق میبینه که روی کدوم node این event رخ داده و listener اون رو صدا میکند.

۳. فاز سوم هم Bubbling هست که در مثال ابتدایی باهم دیدیم، در این فاز از target event node به بالا همه ی node های این مسیر رو که دارای event handler یکسان هستند رو پیدا و دونه دونه به ترتیب از پایین به بالا اون ها رو صدا میزند یعنی به صورت down top عمل می کند، برعکس فاز اول.

خوب فکر میکنم با تعریف این ۳ فاز کاملا مشخص شده باشه که جاوا اسکریپت چه سیستمی برای صدا زدن event listener ها داره و میتونیم از موارد نا خواسته توی برنامه ای که مینویسیم جلو گیری کنیم.

امیدورام این پست برای شما مفید بوده باشه :)

اگر این پست رو دوست داشتید لطفا لایک بفرمایییید ;)