در این بخش از سری آموزش جاوا اسکریپت قصد داریم در مورد Event Propagation یا همان توزیع رویداد در جاوا اسکریپت صحبت کنیم.
درک Event Propagation در جاوا اسکریپت
توزیع رویداد در جاوا اسکریپت یک مکانیزم برای تعریف اینکه چگونه رویدادها بین درخت DOM برای رسیدن به هدف یا چیزی که قرار است اتفاق بیافتد, توزیع یا حرکت کنند.
بیایید برای درک بهتر از یک مثال کمک بگیریم. فرض کنید یک هندلر رویداد کلیک را روی هایپر لینک (المان <a>
) که داخل یک پاراگراف (المان <p>
) ست کردید.
حالا اگر روی لینک کلیک کنید, هندلر اجرا می شود اما به جای لینک, اگر هندلر رویداد کلیک را روی پاراگرف شامل لینک قرار بدید, پس حتی در این مورد, کلیک روی لینک, هندلر را فراخوانی می کند.
چراکه رویدادها فقط روی المان هذف که رویداد را می سازند اثر نمی گذارد بلکه بین بالا و پایین درخت DOM برای رسیدن به هدف حرکت می کنند. این همان Event Propagation یا توزیع رویداد در جاوا اسکریپت است.
در مرورگرهای مدرن Event Propagation حاصل دو مفهوم capturing و bubbling هستند
تصویر بالا نحوه حرکت رویداد بین درخت DOM در طول فازهای مختلف توزیع رویداد زمانی که یک رویداد روی المانی که المان والد دارد را نشان می دهد.
مفهوم توزیع رویداد در جاوا اسکریپت برای زمان هایی که چندین المان در DOM با رابطه والد-فرزند دارای یک هندلر رویداد برای رویداد یکسان مثل کلیک ماوس, معرفی شده است.
حالا سوال اینجاست که کدام رویداد کلیک زمانی که کاربر روی المان داخلی کلیک می کنید, اول مدیریت می شود
در این بخش در مورد فازهای توزیع رویداد با جزییات بیشتر و پیدا کردن جواب این سوال صحبت خواهیم کرد.
نکته: بطور رسمی ۳ فاز capture, target, bubble وجود دارد اما دومین فاز, target (زمانی اتفاق می افتد که رویداد به المان هدفی که رویداد ساخته شده را دارد برسد) در مرورگرهای مدرن بصورت جداگانه مدیریت نمی شود, هندلرهای مشخص شده برای هر دو فاز capture, bubble برای این فاز نیز اجرا می شود.
فاز capturing
در این فاز, توزیع رویداد از window
به پایین توسط درخت DOM به گره هدف حرکت می کند.
برای مثال, اگر کاربر روی یک لینک کلیک کرد, رویداد کلیک توسط المان <html>
, المان <body>
و المان <p>
شامل لینک, پاس داده می شود.
همچنین اگر اجداد (والد, ..) المان هدف و خود المان مخصوصا capturing event listener برای نوع رویداد داشته باشند, آن listener ها در طول این فاز اجرا می شود.
مثال زیر را ببنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Event Capturing Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div id="wrap">DIV <p class="hint">P <a href="#">A</a> </p> </div> <script> function showTagName() { alert("Capturing: "+ this.tagName); } var elems = document.querySelectorAll("div, p, a"); for(let elem of elems) { elem.addEventListener("click", showTagName, true); } </script> </body> </html> |
در مثال بالا نحوه کارکردن capturing رویداد را می بینید.
capturing رویداد فقط روی هنلدرهای رویداد که با متد addEventListener()
, ایجاد شده باشند , مقدار آرگومان سوم آنها true
است کار می کند.
فاز bubbling
فاز Bubbling دقیقا برعکس عمل می کند. در این فاز توزیع رویداد در جاوا اسکریپت از همان المان به عقب بر می گردد. در بالا دیدید که از المان ریشه به المان هدف می آید ولی اینجا از المان هدف به المان ریشه بر می گردد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Event Bubbling Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div onclick="alert('Bubbling: ' + this.tagName)">DIV <p onclick="alert('Bubbling: ' + this.tagName)">P <a href="#" onclick="alert('Bubbling: ' + this.tagName)">A</a> </p> </div> </body> </html> |
دسترسی به المان هدف
المان هدف یک گره DOM است که توسط رویداد ساخته می شود. برای مثال, اگر کاربر روی لینک کلید کند, المان هدف همان لینک است.
المان هدف توسط event.target
قابل دسترس است. این المان در فازهای توزیع رویداد در جاوا اسکریپت تغییر نمی کند. به علاوه, کلمه کلیدی this
نشان دهنده المان فعلی (المانی که در حال حاضر هندلر متصل به آن در حال اجرا می باشد) است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Event Target Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div id="wrap">DIV <p class="hint">P <a href="#">A</a> </p> </div> <script> // Selecting the div element var div = document.getElementById("wrap"); // Attaching an onclick event handler div.onclick = function(event) { event.target.style.backgroundColor = "lightblue"; // Let the browser finish rendering of background color before showing alert setTimeout(() => { alert("target = " + event.target.tagName + ", this = " + this.tagName); event.target.style.backgroundColor = '' }, 0); } </script> </body> </html> |
علامت =>
استفاده شده در مثال بالا, فلش یک function expression است. این همان سینتکس کوتاه برای ایجاد توابع expression می باشد و باعث می شود کلمه کلیدی this
به خوبی رفتار کند.
متوقف کردن توزیع رویداد
همچنین اگر می خواهید از اینکه هنلدر رویداد المان های والد درباره آن رویداد باخبر نشوند, می توانید در این حین توزیع رویداد را متوقف کنید.
برای مثال, فرض کنید که المان های تو در تو دارد و هر المان یک رویداد onclick
برای نمایش باکس پیام دارد. بطور عادی زمانی که کاربر روی المان های داخلی کلیک می کند, چون رویداد در درخت DOM به بالا می رود, همه هندلرها به یکباره اجرا می شوند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Event Propagation Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div id="wrap">DIV <p class="hint">P <a href="#">A</a> </p> </div> <script> function showAlert() { alert("You clicked: "+ this.tagName); } var elems = document.querySelectorAll("div, p, a"); for(let elem of elems) { elem.addEventListener("click", showAlert); } </script> </body> </html> |
برای جلوگیری از این مشکل می توانید از متد event.stopPropagation()
برای متوقف کردن آن استفاده کنید.
در مثال زیر اگر روی المان های فرزند کلیک کنیم., event listener کلیک روی المان والد اجرا نمی شود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Stop Event Propagation Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div id="wrap">DIV <p class="hint">P <a href="#">A</a> </p> </div> <script> function showAlert(event) { alert("You clicked: "+ this.tagName); event.stopPropagation(); } var elems = document.querySelectorAll("div, p, a"); for(let elem of elems) { elem.addEventListener("click", showAlert); } </script> </body> </html> |
به علاوه, می توانید اجرای دیگر listener های متصل شده به المان یکسان با نوع رویداد یکسان را با متد stopImmediatePropagation()
متوقف کنید.
در مثال زیر ما چندین listener را به لینک متصل کردیم اما فقط یکی از آنها با کلیک روی لینک اجرا می شوند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Stop Immediate Propagation Demo</title> <style type="text/css"> div, p, a{ padding: 15px 30px; display: block; border: 2px solid #000; background: #fff; } </style> </head> <body> <div onclick="alert('You clicked: ' + this.tagName)">DIV <p onclick="alert('You clicked: ' + this.tagName)">P <a href="#" id="link">A</a> </p> </div> <script> function sayHi() { alert("Hi, there!"); event.stopImmediatePropagation(); } function sayHello() { alert("Hello World!"); } // Attaching multiple event handlers to hyperlink var link = document.getElementById("link"); link.addEventListener("click", sayHi); link.addEventListener("click", sayHello); </script> </body> </html> |
نکته: اگر چندین event listener به المان یکسان با نوع رویداد یکسان متصل شده باشد, آنها به ترتیب اضافه شدن, اجرا می شوند اما اگر هر listener متد event.stopImmediatePropagation()
را صدا بزند, دیگر هیچ listener ی اجرا نمی شود.
جلوگیری از عملیات پیش فرض
بعضی رویدادها عملیات پیش فرضی را به همراه خود دارند. برای مثال زمانی که روی یک لینک کلیک می کند شما راه لینک مقصد می برد یا زمانی که در یک فرم روی دکمه submit کلیک می کنید, فرم ارسال می شود و..
می توانید چنین عملیات پیش فرضی را با استفاده از متد preventDefault()
از آبجکت رویداد متوقف/جلوگیری کنید.
به هرحال, برای جلوگیری از این عملیات, به هیچ وجه توزیع رویداد در جاوا اسکریپت را متوقف نکنید; رویداد طبق معمول برای توزیع در درخت DOM ادامه می یابد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Prevent Default Demo</title> </head> <body> <form action="/examples/html/action.php" method="post" id="users"> <label>First Name:</label> <input type="text" name="first-name" id="firstName"> <input type="submit" value="Submit" id="submitBtn"> </form> <script> var btn = document.getElementById("submitBtn"); btn.addEventListener("click", function(event) { var name = document.getElementById("firstName").value; alert("Sorry, " + name + ". The preventDefault() won't let you submit this form!"); event.preventDefault(); // Prevent form submission }); </script> </body> </html> |
امیدواریم در این بخش آموزش جاوا اسکریپت, از آموزش توزیع رویداد در جاوا اسکریپت نهایت استفاده را برده باشد.
هر سوالی داشتید ، از قسمت نظرات ارسال کنید . سریعا ، پاسخگوی سوالات شما هستیم .
موفق باشید.
باسلام وقت بخیر
یه سوال داشتم در مورد . برای activ نشان دادن دکمه ، لینک های که href دارند و بعد از کلیک رفرش میشوند راحلی وجود دارد که فعال نشان بدیم . لطفا راهنمایی کنید
سلام. ممنون.
میتونید از کوکی ها استفاده کنید.
موفق باشید.