بازگشت به دوره

آشنایی با رویدادها در جاوا اسکریپت

در فصل قبل با بخشی از مدل DOM آشنا شدیم که امکان برقراری ارتباط بین جاوا اسکریپت و صفحه‌ی وب را فراهم می‌کرد. در این فصل به بررسی رویدادها می‌پردازیم که بخش دیگری از DOM هستند و امکان برقراری ارتباط بین کاربر و صفحه‌ی وب را فراهم می‌کنند. می‌توان گفت که بحث اصلی این کتاب از این فصل آغاز می‌شود و در فصول گذشته بیشتر به بررسی مفاهیم اولیه و اصول جاوا اسکریپت پرداخته بودیم. اما از این فصل با جنبه‌های عملی و کاربردی جاوا اسکریپت آشنا خواهیم شد.

در این فصل به بررسی این مباحث می‌پردازیم :  

معرفی رویدادها

تمام برنامه‌هایی که پیش از این در فصول گذشته نوشته بودیم از نوع دستوری (Imperative) بودند. در برنامه‌نویسی دستوری، روند اجرای برنامه کاملاً از پیش تعیین شده است و دستورات یکی پس از دیگری اجرا می‌شوند تا برنامه به پایان رسیده و نتیجه‌ی نهایی تولید شود. در این نوع برنامه‌ها معمولاً داده‌ها از قبل به برنامه داده شده‌اند و برنامه فقط باید آنها را پردازش کرده و نتیجه را نمایش دهد. مثلاً برنامه‌ای را در نظر بگیرید که ضرایب یک چند جمله‌ای را دریافت کرده و ریشه‌های چند جمله‌ای را محاسبه می‌کند. در برخی موارد نیز ممکن است در نقاط خاصی از برنامه، اجرای برنامه متوقف شده و داده‌هایی از کاربر دریافت شوند. سپس ادامه‌ی دستورات اجرا شوند.

اما در این فصل با نوع دیگری از برنامه‌ها آشنا می‌شویم که برنامه‌های رویداد محور (Event-Driven) نام دارند. در این نوع برنامه‌ها عملکرد برنامه وابسته به عوامل خارجی است و دقیقاً نمی‌توان گفت کدهای برنامه با چه ترتیبی اجرا خواهند شد. زیرا ترتیب اجرای کدها، به ترتیب رویدادهای خارجی وابسته است. با توجه به محیط اجرای این برنامه‌ها و کاربرد آنها، نوع رویدادها نیز متفاوت است. مثلاً در یک محیط صنعتی، رویدادها معمولاً توسط حسگرها (Sensors) تولید می‌شوند. مثلاً یک حسگر می‌تواند دمای محیط را اندازه‌گیری کند و به محض رسیدن دمای محیط به یک درجه حرارت خاص، رویدادی را برای برنامه ارسال کند تا برنامه عکس‌العمل مناسب را انجام دهد.

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

تقریباً تمام برنامه‌های کاربردی که با جاوا اسکریپت برای محیط وب نوشته می‌شوند رویداد محور هستند. البته رویدادها محدود به کلیک کردن کاربر نیستند. انواع محتلفی از رویدادها در جاوا اسکریپت تعریف شده‌اند که از مهمترین موارد می‌توان به رویدادهای ماوس، رویدادهای صفحه کلید، رویدادهای لمسی (Touch Events)، رویدادهای Ajax و ... اشاره کرد. که در این فصل به بررسی برخی از این موارد می‌پردازیم.

 

مفهوم Event Handler و Event Listener

در برنامه‌نویسی رویداد محور دو اصطلاح Event Handler (یا مدیر رویداد) و Event Listener کاربرد زیادی دارند. با توجه به این که تشریح این دو مفهوم در کدهای جاوا اسکریپت کمی دشوار است، ابتدا این دو مفهوم با یک مثال از دنیای واقعی تشریح می‌شوند. سپس معادل آنها در کدهای جاوا اسکریپت نشان داده می‌شود.

فرض کنید فردا صبح باید در آزمون کنکور شرکت کنید. به همین دلیل قصد دارید فردا صبح رأس ساعت ۶ بیدار شوید. معمولاً برای اطمینان از بیدار شدن به موقع، از زنگ ساعت استفاده می‌شود. بنابراین زنگ ساعت را برای ساعت ۶ صبح تنظیم می‌کنید و با خیال راحت به رخت خواب می‌روید. زیرا اطمینان دارید که صدای زنگ ساعت، شما را رأس ساعت ۶ بیدار خواهد کرد. اما آیا ساعت شما هم مثل شما با خیال راحت تا ساعت ۶ می‌خوابد؟ خیر.

در واقع ساعت شما باید تا صبح بیدار مانده و وضعیت را بررسی کند. یعنی باید در بازه‌های زمانی بسیار کوتاه (مثلاً هر یک ثانیه) وضعیت را بررسی کند و در صورت فرا رسیدن زمان مورد نظر، با به صدا در آوردن زنگ، شما را از خواب بیدار می‌کند. هر دو مفهوم Event Handler و Event Listener و همچنین مفهوم رویداد را می‌توان در این سناریو مشاهده کرد.

پس به طور خلاصه می‌توان گفت که Event Listener همیشه منتظر وقوع رویداد است و به محض وقوع رویداد، آن را به اطلاع Event Handler می‌رساند. Event Handler نیز به محض دریافت پیام، عکس‌العمل مناسب را برای مدیریت شرایط پیش آمده انجام می‌دهد.

 

نحوه‌ی تعریف Event Handler و Event Listener

جهت تعریف یک Event Listener برای یک عنصر خاص، ۳ روش مختلف وجود دارد. معمول‌ترین و بهترین روش، استفاده از متد addEventListener است. این متد یک Event Listener را برای یک رویداد خاص تعریف می‌کند و یک Event Handler برای مدیریت آن رویداد تعیین می‌کند. نحوه‌ی استفاده از این متد در قطعه کد زیر نشان داده شده است. (فرض کنید عنصری در صفحه‌ی وب وجود دارد که صفت id آن برابر با "button" است.)


const button = document.getElementById('button');
button.addEventListener('click' , handler);

function handler(){
	console.log('Button Clicked!');
}

حال با کلیک کردن روی دکمه‌ی مورد نظر، عبارت "!Button Clicked" در کنسول نمایش داده می‌شود. توجه کنید که هر بار که عمل کلیک انجام می‌شود، یک بار رویداد click اتفاق می‌افتد و به دنبال آن یک بار تابع handler اجرا شده و خروجی را در کنسول نمایش می‌دهد. این برنامه را می‌توانید اینجا در CodePen اجرا کنید.

در قطعه کد فوق، آرگومان اول متد addEventListener نوع رویداد را مشخص می‌کند که در این مثال از رویداد click استفاده شده است. رویداد click معمولاً بیشترین کاربرد را در برنامه‌های جاوا اسکریپت دارد. اما در جاوا اسکریپت بیش از ۸۰ نوع رویداد مختلف پشتیبانی می‌شود که لیست کامل این رویدادها را می‌توانید اینجا ببینید. البته این لیست فقط شامل رویدادهای DOM یا DOM Events است. در جاوا اسکریپت انواع دیگری از رویدادها نیز وجود دارند که بخشی از رویدادهای DOM نیستند. مثلاً رویدادهای Ajax جزئی از رویدادهای DOM نیستند. در این فصل به بررسی برخی از این رویدادها می‌پردازیم، و موارد دیگری نیز به مرور در فصل‌های بعدی معرفی خواهند شد.

اما آرگومان دوم متد addEventListener یک Callback Function است. این آرگومان تابعی را مشخص می‌کند که در زمان وقوع رویداد باید اجرا شود. این تابع در واقع نقش Event Handler را بازی می‌کند. زیرا در صورت وقوع رویداد، فراخوانی شده و عملیات لازم را انجام می‌دهد. اما نمی‌توان کد مربوط به Event Listener را در این مثال مشخص کرد. زیرا Event Listener ها در پشت صحنه عمل می‌کنند و توسط مرورگر مدیریت می‌شوند. یعنی متد addEventListener از مرورگر درخواست می‌کند که یک Event Listener برای عنصر button و رویداد click ایجاد کند.

نکته : با توجه به این که در کدهای جاوا اسکریپت دقیقاً نمی‌توان Event Listener و Event Handler را از یکدیگر تفکیک کرد، معمولاً این دو مفهوم به صورت معادل به کار برده می‌شوند و برنامه‌نویسان جاوا اسکریپت معمولاً بین این دو تمایز خاصی قائل نمی‌شوند. (مشابه دو مفهوم آرگومان و پارامتر در توابع)

نکته : تابعی که به عنوان Event Handler در آرگومان دوم مشخص می‌شود می‌تواند از هر نوعی باشد. یعنی می‌توان مانند این مثال از Function Declaration برای تعریف تابع استفاده کرد و نام آن را به عنوان آرگومان دوم به متد addEventListener ارسال کرد. همچنین می‌توان از روش Function Expression برای تعریف تابع استفاده کرد. روش دیگر این است که تابع مورد نظر را به صورت بی‌نام در محل آرگومان دوم متد addEventListener تعریف کرد. در این صورت نیز برای تعریف تابع می‌توان از Function Expressions و یا Arrow Functions استفاده کرد. استفاده از Arrow Function ها به عنوان Event Handler بسیار مرسوم است.

مثال فوق را می‌توان با یک Arrow Function به صورت زیر بازنویسی کرد.


const button = document.getElementById('button');
button.addEventListener('click' , () => console.log('Button Clicked!'));
 

معرفی تابع alert

در برنامه‌های مبتنی بر رویداد، استفاده از کنسول برای نمایش خروجی‌ها روش مناسبی نیست. به همین دلیل از این به بعد در اکثر موارد برای نمایش خروجی‌های متنی، از تابع alert استفاه خواهیم کرد. تابع alert در واقع بخشی از مدل BOM است که در فصل ۹ با جزئیات آن آشنا خواهیم شد. فعلاً فقط تابع alert از BOM را معرفی می‌کنیم که وظیفه‌ی آن نمایش یک پیام متنی به کاربر، در قالب یک جعبه پیام (Dialog Box) است.

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

const button = document.getElementById('button');
button.addEventListener('click' , () => alert('Button Clicked!'));

حال با کلیک کردن روی دکمه، یک جعبه‌ی پیام به صورت زیر نمایش داده می‌شود. البته ظاهر این جعبه‌ی پیام در مرورگرهای مختلف کمی متفاوت است. شکل زیر مربوط به مرورگر Firefox است. این برنامه را می‌توانید اینجا اجرا کنید. (عبارتی که در بالای عبارت "!Buttion Clicked" نمایش داده شده است، نام فایلی که صفحه‌ی وب در آن قرار دارد را نشان می‌دهد.)

alert box

 

سایر روش‌های تعریف Event Listener ها

همانطور که اشاره شد بهترین روش برای تعریف Event Listener ها استفاده از متد addEventListener است. اما در جاوا اسکریپت دو روش دیگر نیز برای انجام این کار وجود دارد که در گذشته به کار برده می‌شدند و هنوز هم پشتیبانی می‌شوند.

هرچند توصیه می‌شود که همیشه از متد addEventListener برای تعریف Event Listener ها استفاده کنید. اما بهتر است با دو روش دیگر نیز آشنا باشید تا در صورت مواجه شدن با کدهای قدیمی جاوا اسکریپت که از این روش‌ها استفاده می‌کنند سردرگم نشوید.

به ازای تمام رویدادهای DOM، عناصر صفحه‌ی وب دارای خاصیتی به نام همان رویداد هستند (البته باید پیشوند "on" را به نام رویداد اضافه کرد). مثلاً برای رویداد click، تمام عناصر خاصیتی به نام onclick دارند. هر تابعی که به این خاصیت نسبت داده شود. به عنوان Event Handler برای رویداد click به کار برده می‌شود. قطعه کد زیر نحوه‌ی استفاده از این روش را نشان می‌دهد که عملکردی دقیقاً مشابه مثال قبل دارد.


const button = document.getElementById('button');
button.onclick = function(){
	alert('Button Clicked!');
}

البته استفاده از این روش ساده‌تر به نظر می‌آید و در صورت استفاده از یک Arrow Function، ساده‌تر هم می‌شود. اما این روش یک محدودیت هم دارد. در این روش برای هر رویداد فقط می‌توان یک Event Handler تعریف کرد. یعنی اگر در محل دیگری از برنامه، مجدداً تابع جدیدی را به خاصیت onclick عنصر button نسبت دهیم، Event Handler قبلی از بین می‌رود. اما در صورت استفاده از متد addEventListener می‌توان به تعداد دلخواه برای یک رویداد خاص Event Handler تعریف کرد. البته معمولاً نیازی به تعریف بیش از یک Event Handler برای یک رویداد نخواهیم داشت. اما بهتر است به استفاده از متد addEventListener عادت کنید تا در صورت نیاز بتوانید بیش از یک Event Handler برای یک رویداد خاص تعریف کنید.

اما روش دیگری که برای تعریف Event Listener ها به کار می‌رود، استفاده از صفاتی است که با پیشوند "on" شروع می‌شوند. مثلاً برای رویداد click می‌توان از صفت onclick استفاده کرد. در این روش می‌توان نام یک تابع (به عنوان Event Handler) یا یک دستور جاوا اسکریپت را به عنوان مقدار صفت onclick به کار برد. در این حالت نیز در صورت وقوع رویداد click، دستور یا تابع مورد نظر اجرا خواهد شد. قطعه کد زیر نحوه‌ی انجام این کار را نشان می‌دهد.


<input type="button" onclick="handler()" value="Click me" />

در این حالت نیز با کلیک کردن روی این دکمه، تابع handler اجرا می‌شود. در این روش نیز مانند روش قبلی فقط می‌توان از یک Event Handler برای هر رویداد استفاده کرد. همچنین در این روش به دلیل ترکیب شدن کدهای HTML و جاوا اسکریپت، خوانایی برنامه کاهش یافته و نگهداری برنامه سخت‌تر می‌شود. بنابراین استفاده از این روش به هیچ وجه توصیه نمی‌شود.

در این بخش با مقدمات و مفاهیم پایه‌ای مربوط به رویدادها آشنا شدیم. در بخش‌های بعدی این فصل با جزئیات بیشتری در مورد رویدادها و انواع آنها صحبت خواهیم کرد.