From the very beginning JavaScript had to fight the various browser incompatibilities and this is still an issue today.
In this blog I'd like to highlight some of the issues with JavaScript mouse events.
Nothing new for the experienced JavaScript developer but an issue of confusion for newcomers. And I won't go into the IE and Microsoft event model (look here for a good description) differences but restrict myself to Firefox and Safari.
My experimental setup consists of two SELECT boxes called Left and Right.
The Left box contains one OPTION element.
The boxes and the option are coloured to highlight their boundaries.
A TEXTAREA shows the log of events (and can be cleared with the Clear log button).
The SELECT boxes have been set up to capture MOUSEOVER, MOUSEDOWN and MOUSEUP events. There is one line in the log for each event consisting of:
- the type of event (MOUSEOVER etc.)
- the type of element which caused the event (OPTION or SELECT)
- the name (if SELECT) or value (if OPTION) of the event
- the element which triggered the mouse event handler
Experiment 1a
Put your mouse pointer at the title
Left and move the mouse down to the blue area of the Left box in a normal tempo.
Watch the event log.
You see three MOUSEOVER events:
one for the SELECT box: this is because there is a tiny blue area between the top of the SELECT box and the top of the OPTION box.
one for the OPTION and
one for the SELECT box again.
No surprises so far, here are the results (I tested onn Firefox 16.0.2 and Safari 5.0.6)
MOUSEOVER SELECT Left (triggered by Left)
MOUSEOVER OPTION item 1 (triggered by Left)
MOUSEOVER SELECT Left (triggered by Left)
Eperiment 1b
Like before but move your mouse fast.
You'll notice that the first (and sometimes even the second) MOUSEOVER event has disappeared i.e. you need to understand that the ways users move the mouse influence which events are fired.
Some events don't show up even when you think they should have happened.
If you implement dependencies on certain events you might go wrong if these events don't occur.
Experiment 2
In this experiment I explore the MOUSEDOWN and MOUSEUP events, in particular when there is some more action in between DOWN and UP.
2a
Start again at the title Left and move your mouse onto the OPTION box, click and release the left mouse button.
There should be two MOUSEOVER events like before for SELECT and OPTION (which I will not display below) and two new events for the OPTION element: one MOUSEDOWN and one MOUSEUP
MOUSEDOWN OPTION item 1 (triggered by Left)
MOUSEUP OPTION item 1 (triggered by Left)
2b
Start again at the title Left and move your mouse onto the OPTION box, click the left mouse button and hold it, drag the mouse further down into the blue area of Left and release the mouse button.
The MOUSEDOWN event should be like before.
There is one new MOUSEOVER event for entering the SELECT box and the MOUSEUP event has changed: its target is now the SELECT box rather than the OPTION box like before.
MOUSEDOWN OPTION item 1 (triggered by Left)
MOUSEOVER SELECT Left (triggered by Left)
MOUSEUP SELECT Left (triggered by Left)
2c
Start again at the title Left and move your mouse onto the OPTION box, click the left mouse button and hold it, drag the mouse to the right into the blue area of the second SELECT box Right and release the mouse button there.
The first MOUSEOVER event should be like before.
Then there are three events:
a MOUSEOVER in Left for entering the SELECT box after leaving the OPTION box to the right
a MOUSEOVER in Right for entering the second SELECT box
a MOUSEUP event.
Here we find important differences in the way browsers handle these events:
the order in which the events are logged is different
the target of the MOUSEUP event is different
Firefox 16.0.2
|
MOUSEDOWN OPTION item 1 (triggered by Left)
MOUSEOVER SELECT Left (triggered by Left)
MOUSEUP SELECT Left (triggered by Left)
MOUSEOVER SELECT Right (triggered by Right)
|
Safari 5.0.6
|
MOUSEDOWN OPTION item 1 (triggered by Left)
MOUSEOVER SELECT Left (triggered by Left)
MOUSEOVER SELECT Right (triggered by Right)
MOUSEUP SELECT Right (triggered by Right)
|
Compared to Firefox
the last two events are switched and the MOUSEUP event's target is Right (not Left)
i.e. during the MOUSEUP event Safari takes the current position of the mouse as an event target whereas Firefox takes the old element where the original MOUSEDOWN happened.
|
Conclusion:
this blog just showed the tip of iceberg when it comes to mouse event handling in JavaScript. Various browsers or browser releases and various event models make it very difficult to write JavaScript programs which are generally valid. The experienced JavaScript developer will point to libraries like jQuery which simplify a lot of these incompatibilities and their study is worth the effort but I always think it is nice to experience some of these issues directly, makes the appreciation of good libraries even higher.
For those interested here is the full code:
<FORM ACTION="nothing" METHOD="POST" NAME="FormObject">
<TABLE BORDER>
<TR><TD ALIGN="CENTER">
Left<BR>
<SELECT NAME="Left"
ONMOUSEDOWN="handle_event(event)"
ONMOUSEOVER="handle_event(event)"
ONMOUSEUP="handle_event(event)"
SIZE="5" STYLE="background: #0000ff; width: 80px;">
<OPTION NAME="Option 1" STYLE="background: #ffff00;" VALUE="item 1">Item 1</OPTION>
</SELECT>
</TD><TD ALIGN="CENTER">
Right<BR>
<SELECT NAME="Right"
ONMOUSEDOWN="handle_event(event)"
ONMOUSEOVER="handle_event(event)"
ONMOUSEUP="handle_event(event)"
SIZE="5" STYLE="background: #0000ff; width: 80px;">
</SELECT>
</TD><TD VALIGN="TOP">Log of mouse events<BR>
<TEXTAREA COLS="50" NAME="TextObject" ROWS="10"></TEXTAREA>
</TD><TD VALIGN="CENTER">
<INPUT ONCLICK="text.value=''" TYPE="button" VALUE="Clear log" >
</TD></TR>
</TABLE>
</FORM>
<SCRIPT>
var text = document.FormObject.TextObject;
function handle_event(e) {
var name;
var target = e.target ;
var curr = e.currentTarget ;
if(target.nodeName=="OPTION") {
name = target.value
} else {
name = target.name
}
text.value = text.value
+ e.type.toUpperCase()
+ " " + target.nodeName + " " + name
+ " (triggered by " + curr.name + ")"
+ "\n";
}
</SCRIPT>
Note: since events seem to be prevented in Blogger preview I needed to test the JavaScript pieces outside of the editing process, a little cumbersome and potentially dangerous when the functionality of your blog can only be fully tested after publishing, oh well.