Event Handler ها در زبان جاوا اسکریپت

04 اردیبهشت 1398
Advanced-Javascript-event-handler

Event Handler چیست؟

در زبان های برنامه نویسی رویدادهای مختلفی وجود دارد و ما در قسمت قبل با برخی از رویدادهای مهم جاوا اسکریپت آشنا شدیم. حالا زمانی که این رویدادها تعریف می شوند، باید چیزی هم تعریف شود که با اجرای آن ها کار خاصی را انجام دهد. به طور مثال در قسمت قبل دیدیم که در کد زیر، عنصر <h1> پس از کلیک کاربر تغییر می کند:

<!DOCTYPE html>
<html>
<body>

<h1 onclick="this.innerHTML='Ooops!'">Click on this text!</h1>

</body>
</html>

مشاهده ی خروجی در JSBin

در این کد و تمام کدهای دیگری که در قسمت های قبلی یاد گرفتیم، جزء مشترکی وجود دارد؛ مشخصا پس از اجرای رویداد میخواهیم کار خاصی را انجام دهیم اما این کار را در همان HTML و به صورت Attribute انجام داده ایم. اگر بخواهیم آن را در خود جاوا اسکریپت پیاده سازی کنیم چطور؟

در زبان های برنامه نویسی به کدی که مسئول مدیریت یک یا چند رویداد (event) است، event handler (به معنی «مدیریت کننده ی رویداد») می گویند.

به طور مثال در کد زیر می خواهیم با کلیک کردن کاربر روی یک دکمه، تاریخ را به او نمایش دهیم:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method to attach a click event to a button.</p>

<button id="myBtn">Try it</button>

<p id="demo"></p>

<script>
document.getElementById("myBtn").addEventListener("click", displayDate);

function displayDate() {
  document.getElementById("demo").innerHTML = Date();
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

همانطور که در مثال بالا می بینید برای ایجاد یک event handler از تابع ()addEventListener استفاده کرده ایم. کار این تابع اضافه کردن event handler به یک عنصر است. البته خوبی آن این است که این کار را بدون حذف کردن یا overwrite کردن رویدادها و event handler های دیگر انجام می دهد، بنابراین می توانید به یک عنصر در آن واحد چندین event handler اضافه کنید تا با اتفاق افتادن یک رویداد چندین عمل مختلف انجام شود.

نکته: شما می توانید چندین event handler را به یک عنصر اضافه کنید بدون اینکه دیگر event handler ها پاک شوند، حتی اگر از یک نوع باشند (به طور مثال همگی از نوع onclick باشند).

البته اضافه کردن event handler به عناصر مختلف محدود نمی شود و می توانید آن را به شیء DOM نیز اضافه کنید (به طور مثال پنجره ی مرورگر). استفاده از event handler ها باعث می شود کدهای HTML از جاوا اسکریپت جدا شوند و در نتیجه کد HTML خوانا تر شود. همچنین به شما اجازه می دهد تا بدون دسترسی به سورس کد HTML به عناصر مختلف اش event handler اضافه کنید.

نکته: برای حذف یک event handler می توانید از تابع ()removeEventListener استفاده کنید.

ساختار کلی ()addEventListener

ساختار کلی تابع  ()addEventListener به این شکل است:

element.addEventListener(event, function, useCapture);

تابع سه پارامتر می گیرد:

  • پارامتر اول، نوع رویداد است. آیا رویداد ما click است یا mousedown و یا ...؟
  • پارامتر دوم، تابعی است که در هنگام اجرا شدن رویداد، صدا زده می شود.
  • پارامتر آخر مقداری boolean است که می گوید از event bubbling استفاده شود و یا event capturing. این پارامتر اختیاری است.

نکته: هنگامی که بخواهیم event handler ای را به صورت یک attribute در سورس کد HTML وارد کنیم از پیشوند On برای رویدادها استفاده می کنیم (مانند onclick یا onmousedown و ...) اما در جاوا اسکریپت چنین کاری خطا محسوب می شود (شکل صحیح click و mousedown و ... است).

حالا وقت بررسی چند مثال شده است. در مثال زیر می خواهیم کاری کنیم که اگر کاربر روی دکمه ما کلیک کرد، پنجره ای باز شود و به او سلام بگوید:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method to attach a click event to a button.</p>

<button id="myBtn">روی این دکمه کلیک کن</button>

<script>
document.getElementById("myBtn").addEventListener("click", function() {
  alert("با عرض سلام به شما کاربر گرامی");
});
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

می توانیم مثال بالا را به شکل دیگری نیز بنویسیم؛ مثلا از تابع های نام دار استفاده کنیم:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method to execute a function when a user clicks on a button.</p>

<button id="myBtn">روی این دکمه کلیک کن</button>

<script>
document.getElementById("myBtn").addEventListener("click", myFunction);

function myFunction() {
  alert ("سلام به شما کاربر گرامی");
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

می بینید که این کار بسیار ساده و راحت است.

اضافه کردن Event Handler های چندگانه

همانطور که قبلا هم گفتیم ()addEventListener می تواند چندین event handler را به صورت همزمان به یک عنصر متصل کند، حتی اگر آن handler ها از یک نوع باشند. به مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method to add two click events to the same button.</p>

<button id="myBtn">روی این دکمه کلیک کنید</button>

<script>
var x = document.getElementById("myBtn");
x.addEventListener("click", myFunction);
x.addEventListener("click", someOtherFunction);

function myFunction() {
  alert ("سلام به شما کاربر گرامی");
}

function someOtherFunction() {
  alert ("این تابع دومی است که اجرا می شود");
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در این مثال تنها از یک نوع رویداد (click) استفاده کردیم. در مثال زیر می خواهیم از 3 رویداد مختلف استفاده کنیم:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method to add many events on the same button.</p>

<button id="myBtn">روی این دکمه کلیک کنید</button>

<p id="demo"></p>

<script>
var x = document.getElementById("myBtn");
x.addEventListener("mouseover", myFunction);
x.addEventListener("click", mySecondFunction);
x.addEventListener("mouseout", myThirdFunction);

function myFunction() {
  document.getElementById("demo").innerHTML += "Moused over!<br>";
}

function mySecondFunction() {
  document.getElementById("demo").innerHTML += "Clicked!<br>";
}

function myThirdFunction() {
  document.getElementById("demo").innerHTML += "Moused out!<br>";
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

توضیح مثال:

  • ابتدا با استفاده از دستور getElementById عنصر مورد نظرمان را گرفته ایم تا بعدا روی آن event handler بگذاریم.
  • سپس با استفاده از دستور addEventListener و سه رویداد مختلف، سه تابع را برای اجرای رویدادها به آن داده ایم.
  • تابع myFunction با هر بار رفتن موس روی دکمه، عبارت Moused over را در صفحه چاپ می کند.
  • تابع mySecondFunction با هر بار کلیک شدن دکمه، عبارت Clicked را در صفحه چاپ می کند.
  • تابع myThirdFunction با هر بار رفتن موس از روی دکمه (خارج شدن موس از محدوده ی دکمه)، عبارت Moused out را در صفحه چاپ می کند.

متصل کردن Event Handler به شیء window

همانطور که میدانید می توانیم با استفاده از addEventListener یک یا چند Event Handler را به شیء window متصل کنیم. به طور مثال در کد زیر می گوییم اگر کاربر ما سایز پنجره ی مرورگر خود را تغییر داد، یک عدد تصادفی را به او نشان بده:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example uses the addEventListener() method on the window object.</p>

<p>Try resizing this browser window to trigger the "resize" event handler.</p>

<p id="demo"></p>

<script>
window.addEventListener("resize", function(){
  document.getElementById("demo").innerHTML = Math.random();
});
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

برای مشاهده ی خروجی، باید به JSbin بروید و سپس سایز مرورگر خود را کوچک و بزرگ کنید.

سوال: اگر بخواهیم هنگام استفاده از addEventListener از تابعی استفاده کنیم که پارامتر می گیرد چکار باید بکنیم؟

پاسخ: در این حالت باید از anonymous function (به معنی «تابع ناشناس») استفاده کنید (ر.ک به این مقاله). به مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript addEventListener()</h2>

<p>This example demonstrates how to pass parameter values when using the addEventListener() method.</p>

<p>Click the button to perform a calculation.</p>

<button id="myBtn">روی این دکمه کلیک کنید</button>

<p id="demo"></p>

<script>
var p1 = 5;
var p2 = 7;

document.getElementById("myBtn").addEventListener("click", function() {
  myFunction(p1, p2);
});

function myFunction(a, b) {
  var result = a * b;
  document.getElementById("demo").innerHTML = result;
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

توزیع رویداد چیست؟

توزیع رویداد یا Event propagation روشی برای تعریف ترتیب رویدادها در هنگام اجرا است. به طور مثال اگر ما یک عنصر <p> داشته باشیم که درون یک عنصر <div> باشد و سپس کاربر روی <p> کلیک کند، رویداد click ابتدا برای کدام اجرا خواهد شد؟ عنصر <p> و یا <div>؟

اگر مقدار روی حالت bubbling تنظیم شده باشد، ابتدا رویدادِ داخلی ترین عنصر انجام می شود؛ بنابراین در مثال فرضی بالا ابتدا رویداد مربوط به عنصر <p> و سپس رویداد مربوط به <div> اجرا خواهد شد. اما اگر مقدار روی حالت capturing باشد، ابتدا رویدادِ خارجی ترین عنصر انجام می شود؛ بنابراین در مثال فرضی بالا ابتدا رویداد مربوط به عنصر <div> و سپس رویداد مربوط به <p> اجرا خواهد شد.

یادتان که هست؟ بالاتر گفتیم که سومین پارامتر تابع addEventListener مقداری boolean است و ما می توانیم با استفاده از آن نوع توزیع رویداد را انتخاب کنیم:

addEventListener(event, function, useCapture);

در قالب بالا به جای useCapture باید از true یا false استفاده کنید. مقدار پیش فرض false است بنابراین در حالت پیش فرض از حالت توزیع bubbling استفاده خواهد شد. اگر مقدار را روی true بگذارید از توزیع capturing استفاده می شود.

به مثال زیر توجه کنید:

<!DOCTYPE html>
<html>
<head>
<style>
#myDiv1, #myDiv2 {
  background-color: coral;
  padding: 50px;
}

#myP1, #myP2 {
  background-color: white; 
  font-size: 20px;
  border: 1px solid;
  padding: 20px;
}
</style>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>

<h2>JavaScript addEventListener()</h2>

<div id="myDiv1">
  <h2>Bubbling:</h2>
  <p id="myP1">روی من کلیک کن</p>
</div><br>

<div id="myDiv2">
  <h2>Capturing:</h2>
  <p id="myP2">روی من کلیک کن</p>
</div>

<script>
document.getElementById("myP1").addEventListener("click", function() {
  alert("شما روی مستطیل سفید رنگ کلیک کرده اید");
}, false);

document.getElementById("myDiv1").addEventListener("click", function() {
  alert("شما روی مستطیل نارنجی رنگ کلیک کرده اید");
}, false);

document.getElementById("myP2").addEventListener("click", function() {
  alert("شما روی مستطیل سفید رنگ کلیک کرده اید");
}, true);

document.getElementById("myDiv2").addEventListener("click", function() {
  alert("شما روی مستطیل نارنجی رنگ کلیک کرده اید");
}, true);
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

هنگامی که به صفحه ی خروجی کد بالا بروید دو جفت مستطیل سفید و نارنجی خواهید دید. یکی از آن ها از توزیع bubbling و دیگری از توزیع capturing استفاده می کند. اگر روی هر کدام کلیک کنید (با توجه کردن به ترتیب پیام هایی که برایتان ظاهر می شود) متوجه می شوید که تفاوت بین این دو توزیع چگونه است.

حذف کردن یک event handler نیز با استفاده از ()removeEventListener به آسانی انجام پذیر است:

<!DOCTYPE html>
<html>
<head>
<style>
#myDIV {
  background-color: coral;
  border: 1px solid;
  padding: 50px;
  color: white;
  font-size: 20px;
}
</style>
</head>
<body dir='rtl'>

<h2>JavaScript removeEventListener()</h2>

<div id="myDIV">
  <p>مربعی که پیش روی شماست دارای رویداد onmousemove است و هر بار که شما موس خود را تکان دهید و مکانش را تغییر دهید، عددی تصادفی به شما نمایش می دهد.</p>
  
  <p>شما می توانید با کلیک کردن روی دکمه ی زیر event handler این رویداد را از بین ببرید</p>
  <button onclick="removeHandler()" id="myBtn">از بین بردن event handler</button>
</div>

<p id="demo"></p>

<script>
document.getElementById("myDIV").addEventListener("mousemove", myFunction);

function myFunction() {
  document.getElementById("demo").innerHTML = Math.random();
}

function removeHandler() {
  document.getElementById("myDIV").removeEventListener("mousemove", myFunction);
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

نکته: توابع ()addEventListener و ()removeEventListener در مرورگر Internet Explorer فقط و فقط از نسخه 9 به بعد پشتیبانی می شوند و در نسخه های 8 و قبل تر کار نخواهند کرد. در این مرورگر های قدیمی میتوانید به جای آن ها از متد های ()attachEvent و ()detachEvent استفاده کنید که به ترتیب همان کار توابع ()addEventListener و ()removeEventListener را می کنند.

به عنوان مثال می توانید کدهایتان را به این شکل بنویسید تا در تمام مرورگر ها اجرا شوند:

<script>
var x = document.getElementById("myBtn");
if (x.addEventListener) {
  x.addEventListener("click", myFunction);
} else if (x.attachEvent) {
  x.attachEvent("onclick", myFunction);
}

function myFunction() {
  alert("Hello World!");
}
</script>

امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری جاوا اسکریپت پیشرفته توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.