ehsan5565ahmadi@gmail.com  | +98 991 181 9828

مفهوم اصل Bubbling در جاوا اسکریپت

مفهوم اصل Bubbling در جاوا اسکریپت

برای اینکه مفهوم مهم Bubbling رو در جاوا اسکریپت یاد بگیریم ابتدا با یک مثال ساده شروع میکنیم.

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

ما در کد بالا اومدیم سه تا تگ ایجاد کردیم. بیرونی ترین تگ ، تگ Form هست سپس تگ div رو داریم که فرزند تگ Form هست ، در نهایت تگ p رو داریم که فرزند تگ p میباشد.

حالا ما اومدیم برای تگ form یک ایونت کلیک ست کردیم و گفتیم که اگه روی فرم کلیک شد عبارت form رو alert بده. همچنین برای تگ div هم یک ایونت ست کردیم و گفتیم وقتی روی div کلیک شد مقدار div رو alert بده و در نهایت برای تگ p هم یک ایونت ست کردیم و گفتیم وقتی روی این تگ کلیک شد مقدار p رو alert بده.

حالا ما پروژه رو اجرا میکنیم ، و روی تگ p کلیک میکنیم . بنظر شما خروجی چی میتونه باشه ؟؟

در ابتدا انتظار داریم که به ما یک alert نمایش داده بشه و عبارت p رو نمایش بده که همین اتفاق هم رخ میده اما در ادامه برای ما به ترتیب alert تگ div و alert تگ form رو هم نمایش میده.

اما چرا این اتفاق رخ داد ؟؟

این اتفاق به خاطر بحثی در جاوا اسکریپت هست به نام Event Delegation که از دو بخش تشکیل شده.

1- Event Bubbling

2- Event Capturing

و حالا ما در این مقاله مبحث Event Bubbling رو توضیح میدیم و در مقاله بعدی مبحث Event Capturing به صورت کامل شرح میدیم.

Bubbling

در مثال بالا که صحبت کردیم در واقع به علت اصل Event Bubbling رخ داده. اما Bubbling به چه معناست؟؟

فرض کنید تعداد المنت تو در تو داریم، برای هر کدام به صورت مجزا ایونت کلیک ست کردیم. مراحل اجرا این ایونت ها به صورت زیر خواهد بود:

1- ابتدا ایونت المنتی که روی آن کلیک شده است اجرا خواهد شد.

2- سپس به سمت المنت بیرونی رفته، ایونت کلیک آن المنت هم در صورت وجود اجرا میشه.

3- به همین صورت به سمت المنت بیرونی حرکت میکنه و در صورت وجود ایونت کلیک، آن ایونت رو اجرا میکنه.

4- تا زمانی که به المنت HTML (بیرونی ترین تگ صفحه) این چرخه تکرار خواهد شد.

حالا ما به صورت تئوری مفهوم Bubbling رو توضیح دادیم حالا بریم سراغ مثال اول و همون مثال رو مرحله به مرحله توضیح بدیم که چه اتفاقی میفته طبق اصل Bubbling تا کامل متوجه مطلب بشیم

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

زمانی که ما روی تگ p کلیک میکنیم به ترتیب اتفاقات زیر رخ میده :

1- ابتدا ایونت کلیک تگ p اجرا میشه و به ما alert تگ p رو نمایش میده.

2- سپس ایونت کلیک والد تگ p یعنی تگ div رو برای ما اجرا میکنه و به ما alert تگ div رو نمایش میده.

3- در نهایت میره تگ form رو که بیرونی ترین تگ هست در این مثال بررسی میکنه و میبینه که ایونت کلیک داره پس به ما alert تگ form رو هم نمایش میده.

4- اگر ما برای body , html ,… هم ایونت کلیک ست میکردیم در اینجا این ایونت ها هم اجرا میشد.

نکته های مهم

1- به صورت پیش فرض از بین دو بخش Event Delegation که در بالا گفتیم به صورت پیش فرض در جاوا اسکریپت Event Bubbling اجرا میشه نه Event Capturing.

2- اغلب ایونت ها اصل Bubbling را دارند و بعضی ایونت ها مثل focus چنین قابلیتی را ندارد.

کاربرد Bubbling در مثال واقعی

فرض کنید ما تعدادی input داریم و میخواهیم برای هر کدام یک ایونت keyup ست کنیم.

    <form action="">
        <input type="text" name="" id="one">
        <input type="text" name="" id="tow">
        <input type="text" name="" id="three">
        <input type="text" name="" id="four">
        <input type="text" name="" id="five">
       
    </form>

برای انجام اینکار ما سه روش داریم که یکی یکی انجام میدیم تا در نهایت از بین این سه روش بهترین روش رو پیدا کنیم.

روش اول اینه که بیایم در سمت جی اس تک تک این اینپوت ها رو انتخاب کنیم سپس برای هر کدام به صورت جداگانه ایونت keyup رو ست کنیم.

به صورت زیر :

const input1 = document.querySelector('#one')
const input2 = document.querySelector('#tow')
const input3 = document.querySelector('#three')
const input4 = document.querySelector('#four')
const input5 = document.querySelector('#five')

input1.addEventListener('keyup', e => {
    console.log(e);
})
input2.addEventListener('keyup', e => {
    console.log(e);
})
input3.addEventListener('keyup', e => {
    console.log(e);
})
input4.addEventListener('keyup', e => {
    console.log(e);
})
input5.addEventListener('keyup', e => {
    console.log(e);
})

در اینجا میبینم که چندین خط کد تکراری نوشتیم و اصلا کد بهینه و تمیزی نیست و همچنین اگه تعداد اینپوت ها رو کم یا زیاد کنیم باید تغییرات اساسی در این کد ها ایجاد کنیم.

روش دوم به این صورت هست که ما میایم همه این اینپوت ها رو در قالب یک آرایه انتخاب میکنیم و سپس با یک حلقه forEach برای هر کدام از اینپوت ها ایونت keyup رو ست میکنیم.

به صورت زیر :

const inputs = document.querySelectorAll('form input')


inputs.forEach(input => {
    input.addEventListener('keyup', e => {
        console.log(e);
    })
})

در این روش که نسبت به روش اول بهینه تر و کوتاه تر هست ولی باز هم برای ما میتونه موقع اضافه کردن ورودی هایی یکسری مشکلات ایجاد کنه.

روش سوم استفاده از Bubbling هست که به صورت زیر انجام میشه.

const form = document.querySelector('form')

form.addEventListener('keyup', e => {
    if(e.target.tagName = 'INPUT', e.target.type = 'text'){
        console.log(e.target);
    }
})

در واقع در این روش ما اومدیم با ست کردن یک ایونت برای تگ والد یعنی تگ form برای برای تمام اعضای داخلی این تگ هم این ایونت به دلیل خاصیت Bubbling اجرا میشه.

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

متوقف کردن Bubbling

برای غیر فعال کردن این خاصیت میتونیم از متد های stopPropagation و stopImmediatePropagation استفاده کنیم.

برای مثال به صورت زیر عمل میکنیم.

<body onclick="alert(`the bubbling doesn't reach here`)">
  <button onclick="event.stopPropagation()">Click me</button>
</body>

در اینجا اومدیم برای body ایونت کلیک ست کردیم که موقعی روی body کلیک شد برای ما یک عبارتی رو با alert نمایش بده و همچنین برای یک دکمه هم ایونت کلیک ست کردیم و داخلش گفتیم که با متد stopPropagation باعث کنسل شدن Bubbling بشه.

و اینجوری وقتی روی دکمه کلیک میکنیم فقط ایونت کلیک دکمه برای ما اجرا میشه و ایونت کلیک body برای ما اجرا نخواهد شد.

دو متد stopPropagation و stopImmediatePropagation تفاوت های ریزی با هم دارند که در مقاله های بعدی به انها میپردازیم.

امیدوارم مطالب گفته شده براتون مفید و قابل فهم بوده باشه ✌❤.

مهدی ایلخانی نسب
طراح و توسعه دهنده وب و علاقه مند به چالش برنامه نویسی.

دیدگاهتان را بنویسید

چطوری میتونم کمکتون کنم؟