اتصال خودکار رویداد به عناصر HTML با استفاده از نام class

تصویر مهدی

مقدمه:

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

راه حل:

راه حل این سوال، استفاده از event delegation است. این شیوه دقیقا همان کاری است که من در اسکریپت تایپ فارسی انجام داده ام و همچنین در دستور live در jQuery اتفاق می افتد. توضیحات نسبتا خوبی در این مورد در اینجا داده شده است.
برای حل این مساله، من یک تابع ساده به صورت زیر نوشتم:
// addEventToClass
// Copyright (C) 2009 M. Mahdi Hasheminezhad (hasheminezhad at gmail dot com)
// Bind events to class names
// This source is licensed under Common Public License Version 1.0 (CPL) 
// History:
// 2009-09-12 First Public Release M. Mahdi Hasheminezhad (http://hasheminezhad.com)
function addEventToClass(eventType, eventHandle, className){
	function runEvent(e){
		if((new RegExp("(^|\\s)" + className + "(\\s|$)")).test((e.target || e.srcElement).className)){
			if(typeof(eventHandle) == 'function') eventHandle(e);
			else eval(eventHandle);
		}
	}
	if (document.addEventListener) document.addEventListener(eventType, runEvent, false);
	else if (document.attachEvent) document.attachEvent("on" + eventType, runEvent);
	else document["on" + eventType] = runEvent;
}
پارامترهای ورودی تابع به ترتیب عبارتند از:
1- نام رویداد (مثلا keyup ،change و ...)
2- تابع مورد نظر (تابعی که قرار است اجرا شود)
3- نام کلاس المنت مورد نظر (کلاسی که تابع به آن متصل می شود)
بعد از اجرا کردن این تابع، به طور خودکار رویداد مورد نظر برای تمام المنتهایی که دارای کلاس ذکر شده هستند، اجرا می شود.


شیوه استفاده:

فرض کنید میخواهید با ورود ماوس به عناصری که دارای کلاس highlight هستند، پیامی نمایش داده شود. به این منظور تابع فوق را به این صورت فراخوانی می کنیم:
addEventToClass("mouseover", function(e){alert('You are here.');}, "highlight ");
همچنین به صورت ساده تر میتوان تابع را به این صورت فراخوانی کرد:
addEventToClass("mouseover", "alert('You are here.');", "highlight ");
از این به بعد هر گاه ماوس در صفحه بر روی هر المنتی که دارای کلاس highlight است برود، پیام مورد نظر نمایش داده می شود.

استفاده از this:

فرض کنید در مثال قبلی بخواهیم که رنگ زمینه عنصر مورد نظر هنگام ورود ماوس بر روی آن تغییر کند. چون در این حالت رویداد به document متصل شده است، دیگر از کلمه کلیدی this نمیتوان استفاده کرد. راه حل این مساله استفاده از پارامتر ورودی رویداد است که به صورت event.target (در IE به صورت event.srcElement) قابل استفاده است. به عنوان نمونه، من کد لازم برای مساله مطرح شده را به این صورت مینویسم:
addEventToClass(
	"mouseover",
	function(e){
		var target = (e.target || e.srcElement);
		target.style.backgroundColor = 'red';
	},
	"highlight ");


مشکلات استفاده از این روش

همانطور که گفته شد، راه حل مطرح شده در اینجا استفاده از event delegation است که خود بعضی مشکلات را به همراه دارد.
اولین مساله استفاده از focus و blur است که به طور پیشفرض در این روش قابل کنترل نیست. راه حل خوبی برای این مشکل در quirksmode ارایه شده است.
مشکل بعدی استفاده از رویداد change است که در IE به طور صحیح پیاده سازی نشده است.

بروزرسانی

دوستی که این سوال را مطرح کرده بود، پرسید که چطور با استفاده از ویژگی دیگری (بجز نام class) این کار را انجام دهد. مثلا در اسکریپت تایپ فارسی من این کار را با ویژگی lang انجام داده بودم. برای این کار تغییرات مختصری در اسکریپت داده ام. اسکریپت درخواست شده به صورت زیر است:
// addEventToAttribute
// Copyright (C) 2009 M. Mahdi Hasheminezhad (hasheminezhad at gmail dot com)
// Bind events to attribute values
// This source is licensed under Common Public License Version 1.0 (CPL) 
// History:
// 2009-09-12 First Public Release M. Mahdi Hasheminezhad (http://hasheminezhad.com)
function addEventToAttribute(eventType, eventHandle, attrName, attrValue){
    function runEvent(e){
        if((e.target || e.srcElement).getAttribute(attrName)==attrValue){
            if(typeof(eventHandle) == 'function') eventHandle(e);
            else eval(eventHandle);
        }
    }
    if (document.addEventListener) document.addEventListener(eventType, runEvent, false);
    else if (document.attachEvent) document.attachEvent("on" + eventType, runEvent);
    else document["on" + eventType] = runEvent;
}
در اینجا پارامتر نام کلاس رو حذف کرده ام و بجای آن دو پارامتر جدید، نام attribute و مقدار آن قرار گرفته است. فکر می کنم هم تغییرات و هم شیوه استفاده نیاز به توضیح بیشتری نداشته باشد.