אירועי DOM בתחביר דמוי #C

שייך לקטגוריות JavaScript
btn.Click+=new EventHandler(btn_Click);
btn.Click+=new EventHandler(btn_Click2);

btn.Click-=new EventHandler(btn_Click);

page.Load+=new EventHandler(page_Load);

מכירים את הסינטקס? כך מצרפים/מורידים אירוע למאזין ב-#C. ניתן ליישם סינטקס זה גם ב-JavaScript בצד לקוח.

בעת כתיבת הסקריפט נתקלתי בכמה קשיים. += היא פעולה המחברת בין מספרים ומשרשרת מחרוזות. שלא כמו ב-#C, אי אפשר לגרום לה לעשות משהו אחר. מכאן נובע, שחייבים להשתמש במספרים או מחרוזות כדי לסכם את כל ה-EventHanlerים המשוייכים לאלמנט. החלטתי על מספרים.

מכיוון שמחלקות מחזירות את עצמן בביטוי ה-new, יש למצוא שיטה להחזיר מספר. השיטה היא שכתוב מתודת valueOf. מתודת ה-valueOf הינה מתודה פנימית שמופעלת בכל פעם שקוראים לערך של אובייקט, כמו למשל בפעולת חיבור, חיסור וכו' (כמו מתודת toString שנקראת אוטומטית בשרשור מחרוזות). הערך שהמתודה תחזיר הוא הערך שיוחזר מביטוי ה-new אל תוך פעולת החיבור או החיסור.

הערך עצמו צריך להיות מזהה ייחודי. השתמשתי בשיטה "בינארית" הבנויה מקומבינציות של חזקות של 2.

קצת על השיטה הבינארית

השיטה מאפשרת למספר פריטים בחזקות של 2 – 1, 2, 4, 8, 16 וכו'. כל חיבור של מספרים אלו ייתן קומבינציה אחת בלבד.
דוגמה:
13 = 8+4+1
26 = 16+8+2
כל חזקה של 2 מייצגת "ביט" במיקום ספציפי.
הביט הראשון הוא 0 והמספר המייצג אותו הוא 1 (2 בחזקת 0)
הביט השני הוא 1 והמספר המייצג אותו הוא 2 (2 בחזקת 1)
הביט השלישי הוא 2 והמספר המייצג אותו הוא 4 (2 בחזקת 2)
וכן הלאה…
כאשר ביט אינו פעיל, הוא אינו נמצא בקומבינציה. כמו ש-2 לא נמצא בקומבינציה של 13.
כדי לבדוק האם ביט "דלוק" בתוך מספר אחר, משתמשים באופרטור & המחזיר את אותו מספר אם הוא דלוק ו-0 אם לא.

בכל פעולת חיבור, הרפרנס לפונקציה מצטרף למערך של כל ה-handlers ומודבק אליו אותו אינדקס בחזקת 2. האינדקס נקבע עפ"י מספר ה-handlers.

לאלמנט שאליו רוצים להוסיף/להוריד events מצמידים מאפיין, נניח Click, המאותחל ל-0. כל += של EventHandler יוסיף לתוך המאפיין Click מספר כלשהו שהוא חזקה של 2. חיבור של כמה EventHandlerים לתוך Click ייתן מספר שפירוק שלו ירכיב קומבינציה אחת בלבד של חזקות של 2.

בנוסף למאפיין, נשייך לאלמנט event בעזרת attachEvent/addEventListener שבהפעלתו נרוץ על כל מערך ה-handlers, ובמידה והמאפיין המספרי מכיל את המזהה של ה-handler מהמערך, נפעיל את הפונקציה.

הסינטקס הסופי הוא כזה:

Event.Register(element1,"Click");
element1.Click+=new EventHandler(handler1); // valueOf returns 1. add 1
element1.Click+=new EventHandler(handler2); // valueOf returns 2. add 2
element1.Click-=new EventHandler(handler2); // valueOf returns 2. subtract 2
element1.Click+=new EventHandler(handler3); // valueOf returns 4. add 4

לאחר כל שורות הקוד, ערכו של המאפיין Click הינו 5. 5, לאחר פירוק בינארי, הופך ל-4+1, שמייצגים "ביטים" 0 ו-2. לכן, הפונקציות באינדקס 0 ו-2 יקראו.

לחיצה על האלמנט תרוץ על מערך ה-handlers ותפעיל את פונקציות 0 ו-2 כי הביטים שלהן דלוקים במאפיין Click.

הקוד במלואו:

<script type="text/javascript">
//<![CDATA[
function EventHandler(handler) {
	this.Handler=handler;
	this._index=EventHandler._allHandlers.length;

	EventHandler._allHandlers.push(this);
}
EventHandler.prototype.valueOf=function () {
	// if function is already in the list, return its value
	for (var i=0;i<EventHandler._allHandlers.length;i++) {
		handler=EventHandler._allHandlers[i];
		if (handler.Handler==this.Handler && handler.registered) return Math.pow(2,i);
	}

	this.registered=true;

	return Math.pow(2,this._index);
};
EventHandler._allHandlers=[];

if (typeof(Event)=="undefined") Event={};
Event.Register=function (element,name) {
	element[name]=0;

	var fire=function (e) { Event.Fire(element[name],e); };

	if (element.attachEvent) element.attachEvent("on"+name.toLowerCase(),fire);
	else element.addEventListener(name.toLowerCase(),fire,false);
};
Event.Fire=function (handlers,e) {
	var idx=0;

	// cache power function for better performance
	var pow=Math.pow;

	for (var i=0;i<EventHandler._allHandlers.length;i++) {
		var idx=pow(2,i);
		// if the current index in the power of 2 contained in the handles list, call the function
		if ((handlers & idx)>0) EventHandler._allHandlers[i].Handler(e);
	}
}

function handler1() { alert(1); }
function handler2() { alert(2); }
function handler3() { alert(3); }
function handler4() { alert(4); }

onload=function () {
	var element1=document.getElementById("element1");
	Event.Register(element1,"Click");
	element1.Click+=new EventHandler(handler1);
	element1.Click+=new EventHandler(handler2);
	element1.Click-=new EventHandler(handler2);
	element1.Click+=new EventHandler(handler3);

	var element2=document.getElementById("element2");
	Event.Register(element2,"Click");
	element2.Click+=new EventHandler(handler1);
	element2.Click+=new EventHandler(handler2);
};
//]]>
</script>
<div id="element1">element1</div>
<div id="element2">element2</div>


קטגוריות

חיפוש

עיקבו אחרי (אקספרימנטלי!)

4 תגובות

כתיבת תגובה

10.01.07 בשעה 21:14
ניר טייב
(אתר)

אהבתי!!!

אני צריך עוד לעבור יותר על הקוד כדי להבין אותו טוב יותר, אבל ממש אהבתי את השיטה.

1
11.01.07 בשעה 6:51
ניר טייב
(אתר)

אגב, אם כבר אתה מבצע כאן תאימות דפדפנים, אפשר לשלוח לevent listener את אובייקט האירוע המתאים:

var fire=function (e) { Event.Fire(element[name],(e||window.event)); };

2
11.01.07 בשעה 8:00
אלעד
ניר טייב אמר/ה:
אגב, אם כבר אתה מבצע כאן תאימות דפדפנים, אפשר לשלוח לevent listener את אובייקט האירוע המתאים.

attachEvent עובד עם event object משלו, ולא עם הגלובאלי (window.event) ממש כמו addEventListener.

הפעמים שכן צריך להתייחס אל window.event הן בשימוש בהשמה של events ללא event listener:

el.onclick=function (e) {
	e=e || window.event;
	// ...
};

3
11.01.07 בשעה 13:35
ניר טייב
(אתר)

וואלה, חידשת לי. תודה.

אגב, ה"זכור פרטים" לא עובד.

4

כתיבת תגובה

תגיות מותרות לשימוש בתוכן
XHTML: אפשר להשתמש בתגים הללו: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre>