Event bubbling یا جوشیدن رویدادها به مفهوم انتقال رویدادهای رخ داده در یک المنت به سمت المنت یا المنتهای والد میباشد. برای مثال با کلیک بر روی یک المنت در صفحه، رویداد کلیک هم در همان المنت اجرا خواهد شد و هم در المنتهای والد.
ساختار سند زیر را در نظر بگیرید:
حال اگر برای هرکدام از divهای موجود در سند، یک هندلر برای مدیریت رویداد کلیک نوشته شود و کاربر بر روی child3 کلیک کند، به ترتیب ابتدا رویداد مربوط به المنت child3 سپس child2 سپس child1 و در نهایت parent اجرا خواهد شد. یعنی با کلیک بر روی child3، تمامی هندلرهای کلیک اجرا خواهند شد. دلیل اینکار همان مفهوم Event bubbling است.
Event bubbling فقط مختص صفحات وب نیست؛ بلکه در تمامی سیستم عاملها یکی از مفاهیم مدیریت رخدادها(Events) است. حتی در برنامههای مبتنی بر ویندوز فرم هم شما با این مفهوم برخورد کردهاید.
در صفحات وب، در نهایت رویدادها به شیء Window منتقل میشوند و در یک وب فرم، به From اصلی برنامه.
حال با این مقدمه به سراغ بهینه سازی کدهای نوشته شدهی خود میرویم. اگر از کتابخانهی جیکوئری استفاده کرده باشید، حتما از رویدادهای مختلف ماوس و صفحه کلید بهره بردهاید. تصور برنامهای که از رویدادها استفاده نکند و باید با کاربر در تعامل باشد، غیرممکن است؛ زیرا این رویدادها هستند که درخواستهای کاربر را به برنامه منتقل میکنند.
به قطعه کد زیر توجه کنید:
ما یک هندلر برای مدیریت رویداد کلیک المنت parent نوشتهایم؛ یکی برای المنت child1 و یکی دیگر برای child2. با استفاده از مفهوم جوشیدن رخدادها میتوانیم هر سه هندلر را حذف و به یک هندلر تبدیل کنیم!
شاید بپرسید مزیت اینکار چیست؟ نکتهی کلیدی در همینجاست. میزان حافظهی مصرفی مدیریت یک رخداد، به مراتب کمتر از چندین رخداد است.
در واقع شما فقط یک هندلر را ثبت و تمامی کارهای لازم را به آن میسپارید. شدیدا توصیه میشود که در نوشتن کدهای خود از ایجاد هندلر بر روی هر عنصر خودداری کنید.
برای مثال اگر شما در صفحهی مدیریت پستها قرار دارید و برای ویرایش هر پست دکمهای را تعیین کرده باشید به جای نوشتن کدی مانند زیر:
از نسخهی بهینه شدهی آن استفاده کنید:
تصور کنید شما در همین صفحه 50 پست را به کاربر نشان داده باشید و اگر از کد بالا استفاده کنید، به ازای هر 50 دکمهی ویرایش، یک هندلر برای رویداد کلیک خواهید داشت. حالا اگر از کد پایین استفاده کنید، تنها یک هندلر برای 50 رویداد خواهید داشت.
همان صفحهی مدیریت پست را در نظر بگیرید. 50 پست داریم. هر کدام یک دکمهی ویرایش، حذف، امتیازات، کامنتها و کلی ابزار دیگر که همه با رویداد کلیک فعال میشوند. چیزی حدود به 300 رویداد را باید ثبت کنید!
این واقعا یک تراژدی بزرگ در مصرف حافظه محسوب میشود. پس بهینهتر است تا با نوشتن یک رویداد کلیک روی کل شیء سند، از ایجاد هندلرهای اضافی خودداری کنید.
در اینجا دو نکته قابل ذکر است:
1- چگونه از Event bubbling جلوگیری کنیم؟
برخی از اوقات لازم است تا در لایههای تو در تو، به ازای هر لایه، کد خاصی اجرا شود. یعنی با کلیک بر روی child3 نمیخواهیم رویداد مربوط به parent یا حتی child2 اجرا شوند. در این حالت باید از event.stopPropagation در بدنهی هندلر استفاده کنیم.
2- چگونه میتوان تشخیص داد که بر روی کدام لایه یا المنت کلیک شده است؟
شما با استفاده از event.event.target، به شیء هدف دسترسی خواهید داشت. برای مثال اگر قصد داشته باشیم که قسمتی از کدهای ما فقط بر روی یک المنت خاص اجرا شوند، میتوانیم به شکل زیر آنها را تفکیک کنیم:
البته نوشتن شرط برای همهی المنتها در یک هندلر هم باعث طولانی شدن کدها و هم تولید کد اضافه خواهد شد. خوشبختانه جی کوئری، مدیریت و ثبت رخدادها را هوشمندانه انجام میدهد. به جای نوشتن شرط، به راحتی کدهای مربوط به هر المنت را در یک رجیستر کنندهی جدا بنویسید و در نهایت جی کوئری آنها را برای شما به یک هندلر منتقل خواهد کرد:
یکی دیگر از مهمترین مزایای کدنویسی به شکل فوق اینست که حتی رویدادهای مربوط به اشیایی که به صورت پویا به سند اضافه میشوند، اجرا خواهند شد.
در صفحهی اصلی همین سایت بر روی دکمهی بارگزاری بیشتر کلیک کنید. پس از اضافه شدن پستها سعی کنید به یک پست امتیاز دهید. اتفاقی نخواهد افتاد. زیرا برای عناصری که بصورت پویا به صفحه اضافه شدهاند رویدادی ثبت نشده است، که اگر از کدهای فوق استفاده شود با کمترین هزینه به هدف دلخواه خود خواهیم رسید.
پس همیشه رویدادها را تا حد امکان بر روی عنصر ریشه تعریف کنید.
دیدن لینک زیر برای اجرای یک تست و درک بهتر مطلب خالی از لطف نخواهد بود:
http://jsperf.com/jquery-body-delegate-vs-document-delegate
ساختار سند زیر را در نظر بگیرید:
<div id="parent"> <div id="child1"> <div id="child2"> <div id="child3"></div> </div> </div> </div>
Event bubbling فقط مختص صفحات وب نیست؛ بلکه در تمامی سیستم عاملها یکی از مفاهیم مدیریت رخدادها(Events) است. حتی در برنامههای مبتنی بر ویندوز فرم هم شما با این مفهوم برخورد کردهاید.
در صفحات وب، در نهایت رویدادها به شیء Window منتقل میشوند و در یک وب فرم، به From اصلی برنامه.
حال با این مقدمه به سراغ بهینه سازی کدهای نوشته شدهی خود میرویم. اگر از کتابخانهی جیکوئری استفاده کرده باشید، حتما از رویدادهای مختلف ماوس و صفحه کلید بهره بردهاید. تصور برنامهای که از رویدادها استفاده نکند و باید با کاربر در تعامل باشد، غیرممکن است؛ زیرا این رویدادها هستند که درخواستهای کاربر را به برنامه منتقل میکنند.
به قطعه کد زیر توجه کنید:
$('#parent').on('click', function (event) { }); $('#child1').on('click', function (event) { }); $('#child2').on('click', function (event) { });
$(document).on('click', '#parent, #child1, #child2, #child3', function (event) { });
در واقع شما فقط یک هندلر را ثبت و تمامی کارهای لازم را به آن میسپارید. شدیدا توصیه میشود که در نوشتن کدهای خود از ایجاد هندلر بر روی هر عنصر خودداری کنید.
برای مثال اگر شما در صفحهی مدیریت پستها قرار دارید و برای ویرایش هر پست دکمهای را تعیین کرده باشید به جای نوشتن کدی مانند زیر:
$('.post .edit').on('click', function (event) { });
$(document).on('click', '.post .edit', function (event) { });
همان صفحهی مدیریت پست را در نظر بگیرید. 50 پست داریم. هر کدام یک دکمهی ویرایش، حذف، امتیازات، کامنتها و کلی ابزار دیگر که همه با رویداد کلیک فعال میشوند. چیزی حدود به 300 رویداد را باید ثبت کنید!
این واقعا یک تراژدی بزرگ در مصرف حافظه محسوب میشود. پس بهینهتر است تا با نوشتن یک رویداد کلیک روی کل شیء سند، از ایجاد هندلرهای اضافی خودداری کنید.
در اینجا دو نکته قابل ذکر است:
1- چگونه از Event bubbling جلوگیری کنیم؟
برخی از اوقات لازم است تا در لایههای تو در تو، به ازای هر لایه، کد خاصی اجرا شود. یعنی با کلیک بر روی child3 نمیخواهیم رویداد مربوط به parent یا حتی child2 اجرا شوند. در این حالت باید از event.stopPropagation در بدنهی هندلر استفاده کنیم.
2- چگونه میتوان تشخیص داد که بر روی کدام لایه یا المنت کلیک شده است؟
شما با استفاده از event.event.target، به شیء هدف دسترسی خواهید داشت. برای مثال اگر قصد داشته باشیم که قسمتی از کدهای ما فقط بر روی یک المنت خاص اجرا شوند، میتوانیم به شکل زیر آنها را تفکیک کنیم:
var elemnt = $(event.target); if (elemnt.attr('id') === 'parent') { alert('this is parnet'); } else if (elemnt.attr('id') === 'child2') { alert('this is child2'); }
$(document).ready(function () { $(document).on('click','#parent', function (event) { }); $(document).on('click','#child1', function (event) { }); $(document).on('click','#child2', function (event) { event.st }); });
یکی دیگر از مهمترین مزایای کدنویسی به شکل فوق اینست که حتی رویدادهای مربوط به اشیایی که به صورت پویا به سند اضافه میشوند، اجرا خواهند شد.
در صفحهی اصلی همین سایت بر روی دکمهی بارگزاری بیشتر کلیک کنید. پس از اضافه شدن پستها سعی کنید به یک پست امتیاز دهید. اتفاقی نخواهد افتاد. زیرا برای عناصری که بصورت پویا به صفحه اضافه شدهاند رویدادی ثبت نشده است، که اگر از کدهای فوق استفاده شود با کمترین هزینه به هدف دلخواه خود خواهیم رسید.
پس همیشه رویدادها را تا حد امکان بر روی عنصر ریشه تعریف کنید.
دیدن لینک زیر برای اجرای یک تست و درک بهتر مطلب خالی از لطف نخواهد بود:
http://jsperf.com/jquery-body-delegate-vs-document-delegate