473,388 Members | 1,600 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,388 developers and data experts.

A Guide to Coding Cross-Browser Scripts Part 2: Event Normalization

kovik
1,044 Expert 1GB
Normalizing Event Triggers in JavaScript

For those of you who are too lazy to keep up with the standards of the current JavaScript versions, this will suffice:

Expand|Select|Wrap|Line Numbers
  1. elem.onmouseup = SomeFunction;
However, that method has a lot of limitations.
  • Only one function per event trigger
  • No control over bubbling/capturing
  • Dynamic addition and removal of events requires creation and deletion of objects
  • It's old

The correct, modern way of handling event triggers are these methods:
  • addEventListener, removeEventListener
  • attachEvent, detachEvent

You see that there are two different ways, right? That's because, yet again, Microsoft decided to impose their own methods on us. In concept, these pairs of functions do the exact same thing, but in reality they don't. Microsoft's way (attachEvent/detachEvent) does not allow you to control whether or not you want to capture events.

The fact that there are two different sets of methods for doing this means that we need to know which ones the browser supports in order to know which ones to use. You could do so with if statements checking whether the browser supports addEventListener or attachEvent, but you'll likely have so many event triggers that it won't be practical. So, I've written two functions do it for you:

Expand|Select|Wrap|Line Numbers
  1. /**
  2. *   Add an event listener to an object
  3. *   @param      object
  4. *   @param      evt         event
  5. *   @param      func            function
  6. *   @param      capture
  7. *   @return     boolean
  8. */
  9. function AddEvent(object, evt, func, capture)
  10. {
  11.     if(typeof func != 'function')
  12.     {
  13.         return false;
  14.     }
  15.     if(object.addEventListener)
  16.     {
  17.         object.addEventListener(evt, func, capture);
  18.         return true;
  19.     }
  20.     else if(object.attachEvent)
  21.     {
  22.         object.attachEvent('on' + evt, func);
  23.         return true;
  24.     }
  25.     return false;
  26. }
  27.  
  28. /**
  29. *   Removes an event listener
  30. *   @param      object
  31. *   @param      evt         event
  32. *   @param      func            function
  33. *   @param      capture
  34. *   @return     boolean
  35. */
  36. function RemoveEvent(object, evt, func, capture)
  37. {
  38.     if(typeof func != 'function')
  39.     {
  40.         return false;
  41.     }
  42.     if(object.removeEventListener)
  43.     {
  44.         object.removeEventListener(evt, func, capture);
  45.         return true;
  46.     }
  47.     else if(object.detachEvent)
  48.     {
  49.         object.detachEvent('on' + evt, func);
  50.         return true;
  51.     }
  52.     return false;
  53. }

Here's how to use them:
Expand|Select|Wrap|Line Numbers
  1. AddEvent(elem, 'mouseup', SomeFunction, false);
  2. RemoveEvent(elem, 'mouseup', SomeFunction, false);
  • The first parameter is an element to add the event listener to
  • The second parameter is the name of the event (without "on")
  • The third parameter is the function (the actual function object with the parentheses)
  • The last parameter is a boolean asking determining whether or not to capture the event

This will simplify your code. The only thing that you have to look out for is that even if you set the last parameter to true, it will still bubble in Internet Explorer instead of capture, so you still have to code for bubbling. Those are the breaks.

These functions will return false if neither of these methods are available, but all modern browsers support at least one of these methods, so it's safe to ignore it.

You should keep both of these events in a global JavaScript file that you use in all of your scripts to simplify event handling.

Original Source: Normalizing Event Triggers in JavaScript


Normalizing Event Objects in JavaScript

Just like almost everything worth using in JavaScript, Microsoft's Event object and the standard Event object are not the same. Because of this, we'll need to normalize the Event object in order to use it.

For those of you who are unfamiliar with JavaScript's Event objects, they are automatically sent to Function objects that are attached to an event trigger as the first parameter, no matter what you do.

The Event object has a lot of different properties, a lot of which are not cross-browser compatible. Differences consist of:
  • layerX/layerY and offsetX/offsetY
  • relatedTarget and fromElement/toElement
  • target and srcElement
  • keyCode and charCode

So, I wrote a function to normalize all of these into this variables (respectively):
  • offsetX/offsetY
  • relatedTarget
  • src
  • key

You can, of course, change the names of the variables in the follow function if you'd like to. This will make it so that you can use event objects without worrying about cross-browser issues.

Expand|Select|Wrap|Line Numbers
  1. /**
  2. *   Gets an event with all needed properties
  3. *   @param      e           event
  4. *   @return     event object
  5. */
  6. function GetEvent(e)
  7. {
  8.     if(!e)
  9.     {
  10.         e               = window.event;
  11.     }
  12.  
  13.     if(e.layerX)
  14.     {
  15.         e.offsetX       = e.layerX;
  16.         e.offsetY       = e.layerY;
  17.     }
  18.  
  19.     if(e.type == 'mouseover' && !e.relatedTarget)
  20.     {
  21.         e.relatedTarget     = e.fromElement;
  22.     }
  23.     else if(e.type == 'mouseout' && !e.relatedTarget)
  24.     {
  25.         e.relatedTarget     = e.toElement;
  26.     }
  27.  
  28.     e.src               = e.srcElement || e.target;
  29.     e.key               = e.keyCode || e.charCode;
  30.  
  31.     return e;
  32. }

Here's how you use it:
Expand|Select|Wrap|Line Numbers
  1. function HandleEvent(e)
  2. {
  3.     // Normalize the event
  4.     e = GetEvent(e);
  5.  
  6.     // Do stuff with the event object
  7. }
And now you can simplify your event handling code by actually changing the event object to be normalized from browser to browser. You should keep this function handy in a file that you include in all of your scripts.

Original Source: Normalizing Event Objects in JavaScript
Jul 2 '07 #1
3 8090
hdanw
61
Why not combine the two?

I have added a table of events so that when the event is fired we can convert event data to "uniform" event format, before the event is fired.

If multiple handlers are attached to a single event for a single object then they are called in the order they were added, and a single instance of the event return data exists for all methods.

Expand|Select|Wrap|Line Numbers
  1. var globalthunkingindex = new Array();
  2.  
  3. function ThunkingHandlerIndexObject(object, evt, func, capture)
  4. {
  5.     this.object = object;
  6.     this.event  = evt;
  7.     this.func = new Array();
  8.     this.func.push(func);
  9.  
  10.     this.capture = capture;
  11. }
  12.  
  13. function FindGlobalThunkingEntry(obj, eventtype)
  14. {
  15.     for( var i = 0; i < globalthunkingindex.length; i++)
  16.     {
  17.         if( obj == globalthunkingindex[i].object)
  18.         {
  19.             // found same object is this event?
  20.             if( eventtype == globalthunkingindex[i].event)
  21.             {
  22.                 return i;
  23.             }
  24.         }
  25.     }
  26.     return -1;
  27. }
  28. function AddHandlerToGlobalThunkingIndex(object, eventtype, func, capture)
  29. {
  30.  // DH added feb 08    
  31.     var index = FindGlobalThunkingEntry(object, eventtype);
  32.     if( index < 0 ) 
  33.     {   // doesn't exist
  34.         globalthunkingindex.push( new ThunkingHandlerIndexObject(object, eventtype, func, capture));
  35.     }
  36.     else
  37.     {
  38.         // does exist, add this function to list 
  39.         globalthunkingindex[index].func.push(func);
  40.     }
  41.  /////////////DH   
  42. }
  43. function RemoveHandlerFromGlobalThunkIndex(obj, eventtype, func)
  44. {
  45.  // DH added feb 08   
  46.     var lindex = FindGlobalThunkingEntry(obj, eventtype);
  47.     if( lindex < 0) return false;
  48.  
  49.     var lobj = globalthunkingindex[lindex];
  50.     if( lobj.func.length > 0) 
  51.     {
  52.         for( var findex = 0; findex < lobj.func.length; findex++)
  53.         {
  54.             if( lobj.func[findex] = func)
  55.             {   
  56.                 // this is our func to remove
  57.                 break;
  58.             }
  59.         }
  60.         lobj.func.splice(findex,1);
  61.     }
  62.     if( lobj.func.length == 0 )
  63.     {
  64.         // list is empty we can remove this node
  65.         globalthunkingindex.splice(lindex,1);
  66.     }
  67. } /////////////DH 
  68.  
  69.  
The previous code was modified to use our event lookup and data normalization.

Expand|Select|Wrap|Line Numbers
  1. /**
  2. *   Add an event listener to an object
  3. *   @param      object
  4. *   @param      evt         event
  5. *   @param      func            function
  6. *   @param      capture
  7. *   @return     boolean
  8. */
  9.  
  10.  
  11. function AddEvent(object, evt, func, capture)
  12. {
  13.     if(typeof func != 'function')
  14.     {
  15.         return false;
  16.     }
  17.  // DH added this line    
  18.     AddHandlerToGlobalThunkingIndex(object, evt, func, capture);
  19.  
  20.     if(object.addEventListener)
  21.     {
  22.         /// DH modified from 
  23.         //  object.addEventListener(evt, func, capture);
  24.         // to 
  25.         object.addEventListener(evt, GlobalThunkHandleEvent, capture);
  26.  
  27.         return true;
  28.     }
  29.     else if(object.attachEvent)
  30.     {
  31.         /// DH modified from 
  32.         //  object.attachEvent('on' + evt, func);
  33.         // to 
  34.         object.attachEvent('on' + evt, GlobalThunkHandleEvent);
  35.  
  36.          return true;
  37.     }
  38.     return false;
  39. }
  40.  
  41. /**
  42. *   Removes an event listener
  43. *   @param      object
  44. *   @param      evt         event
  45. *   @param      func            function
  46. *   @param      capture
  47. *   @return     boolean
  48. */
  49. function RemoveEvent(object, evt, func, capture)
  50. {
  51.     if(typeof func != 'function')
  52.     {
  53.         return false;
  54.     }
  55.  
  56.     if(object.removeEventListener)
  57.     {
  58.         object.removeEventListener(evt, func, capture);
  59.  
  60.       // DH added this line, must occur after event is unattached
  61.         RemoveHandlerFromGlobalThunkIndex( object, evt, func);
  62.  
  63.         return true;
  64.     }
  65.     else if(object.detachEvent)
  66.     {
  67.         object.detachEvent('on' + evt, func);
  68.  
  69.       // DH added this line, must occur after event is unattached
  70.         RemoveHandlerFromGlobalThunkIndex( object, evt, func);
  71.  
  72.         return true;
  73.     }
  74.     return false;
  75. }
  76. /**
  77. *   Gets an event with all needed properties
  78. *   @param      e           event
  79. *   @return     event object
  80. */
  81. function GetEvent(e)
  82. {
  83.     if(!e)
  84.     {
  85.         e               = window.event;
  86.     }
  87.  
  88.     if(e.layerX)
  89.     {
  90.         e.offsetX       = e.layerX;
  91.         e.offsetY       = e.layerY;
  92.     }
  93.  
  94.     if(e.type == 'mouseover' && !e.relatedTarget)
  95.     {
  96.         e.relatedTarget     = e.fromElement;
  97.     }
  98.     else if(e.type == 'mouseout' && !e.relatedTarget)
  99.     {
  100.         e.relatedTarget     = e.toElement;
  101.     }
  102.  
  103.     e.src               = e.srcElement || e.target;
  104.     e.key               = e.keyCode || e.charCode;
  105.  
  106.     return e;
  107. }
  108.  
And finally the code that gets it all done:

Expand|Select|Wrap|Line Numbers
  1. function GlobalThunkHandleEvent(e)
  2. {
  3.     // Normalize the event
  4.     e = GetEvent(e);
  5.     // locate redirection 
  6.     var index = FindGlobalThunkingEntry(e.src, e.type);
  7.     // Do stuff with the event object
  8.     var handlerlist = globalthunkingindex[index].func;
  9.  
  10.     var IsNotMasked = true;
  11.     for( var i =0; i < handlerlist.length; i++)
  12.     {
  13.         if( handlerlist[i](e) == false )
  14.         {
  15.               IsNotMasked = false;
  16.         }
  17.     }// note a single instance of event data exists
  18.      // care must be taken when attempting to mask events
  19.      // 
  20.  
  21. // is this IE only?
  22. // e.returnValue = IsNotMasked;
  23.  
  24.      return IsNotMasked;
  25. }
  26.  
  27.  
This is by no means optimized. It should be elementary to modify the 2 search algorithms to cut down on the time spent looking. Or add a quick hash.

Happy coding.

Dan -
Feb 4 '08 #2
hdanw
61
Why not combine the two?

I[code]
function GlobalThunkHandleEvent(e)
{
// Normalize the event
e = GetEvent(e);
// locate redirection
var index = FindGlobalThunkingEntry(e.src, e.type);
Sorry I haven't tested this on any other browsers. I am using IE.

My concern would be that the "e.type" in function GlobalThunkHandleEvent(e)
may not be as plain as IE? I don't know. Does any KNow if this becomes something like "on"+"mousemove" as apposed to "mousemove"? If so it can be corrected in "GetEvent".

Thanks.

Dan -
Feb 4 '08 #3
acoder
16,027 Expert Mod 8TB
My concern would be that the "e.type" in function GlobalThunkHandleEvent(e)
may not be as plain as IE? I don't know. Does any KNow if this becomes something like "on"+"mousemove" as apposed to "mousemove"? If so it can be corrected in "GetEvent".
It would be "mousemove". See this example.
Feb 7 '08 #4

Sign in to post your reply or Sign up for a free account.

Similar topics

0
by: Web Science | last post by:
Site and Features: http://www.eigensearch.com Search engine, eigenMethod, eigenvector, mathematical, manifolds, science, technical, search tools, eigenmath, Jacobian, quantum, mechanics,...
1
by: Donald Smith | last post by:
Hey, Can anyone tell me how to using cross-coding in .Net? Like In a C# project file, being able to use a VB.NET module. Thanks. Donald
0
by: Web Science | last post by:
Site and Features: http://www.eigensearch.com Search engine, eigenMethod, eigenvector, mathematical, manifolds, science, technical, search tools, eigenmath, Jacobian, quantum, mechanics,...
4
by: Mikkel christensen | last post by:
Hi there I wonder if any of you could point me in a direction where I can find some usefull information about coding standarts. I have some generel experiense in programming but I lack many...
7
by: john_williams_800 | last post by:
Hi; I am thinking of working on a site for a local nonprofit group. Since I will have the option of starting from the beginning I thought I would look into making the site Bobby compliant. ...
23
by: Jeff Rodriguez | last post by:
Here's what I want do: Have a main daemon which starts up several threads in a Boss-Queue structure. From those threads, I want them all to sit and watch a queue. Once an entry goes into the...
0
by: saravanansvks | last post by:
Dear Friends, I have prepared a programme in Visual Basic for SHAPE OPERATOR.But my coding are changing the shape only.It does not changes the fill style of the shape tool .And it is not giving ant...
10
by: ryann18 | last post by:
I have a midterm tomorrow and I need some help! I am not good at Java at all I have the worst teacher on the face of the earth! He tells not teaches! But can someone give me some pointers!! He...
2
by: telsave | last post by:
Hi Hope someone out there can help me. I have just developed my first WEB page to sell an ebook I have written. I have of course included a squeeze page to capture details but what I am looking for...
6
by: ampo | last post by:
Hello. Can anyone help with cross-domain problem? I have HTML page from server1 that send xmlHTTPRequest to server2. How can I do it? Thanks.
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.