Dealing With the Firefox Event Model
I'm writing some HTML at the moment that includes a dynamic pop-up menu. It's only a simple little beast, using some simple HTML and javascript.
Before the preachers start at me, yes, html popup menus are evil. I know this. But sometimes they're useful, and I think that the situation warrants it :) (Although I haven't resolved what to do if javascript is turned off on the client...)
The principles involved here are that when you click on one piece of text, a box appears directly beneath it with your 'menu'. Subsequently, clicking anywhere on the page except for the contents of the pop-up menu causes it to be hidden again.
This is the javascript client side that does it all. It's fairly simple.
<script type="text/javascript">
var openMenu = null;
/* Show or hide a menu.
- Called as a result of clicking on the always visible item.
- sMenuId is the Id of the HTML element to show/hide.
- when a menu is shown, it's id is stored in 'openMenu' for later use
*/
function ToggleMenu(sMenuId) {
if ((openMenu != null) && (openMenu != sMenuId)) {
/* a menu is currently showing, and it's not the one passed in */
CheckMenu(openMenu);
}
/* get the element in question */
var oEL = document.getElementById(sMenuId);
if (oEL) {
switch(oEL.style.display) {
case 'block':
/* it's currently visible. hide it */
oEL.style.display = 'none';
openMenu = null;
if (window.event) {
event.cancelBubble = true;
}
return false;
break;
default:
/* it's hidden. show it. */
oEL.style.display = 'block';
openMenu = sMenuId;
if (window.event) {
event.cancelBubble = true;
}
return false;
}
}
return true;
}
/* Hide the currently shown menu.
- Called as a result of clicking anywhere on the page except for the menu title or the menu itself.
- uses 'openMenu' to reference the currently shown menu.
*/
function CheckMenu() {
if (openMenu == null) {
/* there's no menu visible */
return true;
}
/* get the element in question */
var oEL = document.getElementById(openMenu);
if (oEL) {
/* hide it */
oEL.style.display = 'none';
openMenu = null;
}
}
/* global code chunk. Add a onmousedown handler to the whole document */
if (document.attachEvent) {
document.attachEvent('onmousedown', function(){CheckMenu();});
}
</script>
There's two little tricks I've had to add to this. Firefox has no concept of 'event.cancelBubble', and so I can't call it in that case - it's wrapped in a test for window.event, which IE has but Firefox doesn't.
In the global code chunk, I've wrapped the document wide onmousedown handler in a test for document.attachEvent, again which IE has but Firefox doesn't.
Why have I done this? Well, I'm glad you asked.
As the code exists now, it works perfectly in IE, and it works perfectly in Firefox - except the document wide handler isn't called, and therefore to hide the menu again you have to click on it's title (just like showing it in the first place).
Something in the event model is screwing me up. If i enable it page wide in Firefox, when you click the menu title to show it, it shows the menu, and then the click falls through to the page, where it hides it again. It happens so fast you can't even see it flicker (I know it's happening because I stuck an alert in the CheckMenu() function, which proved it was being called.
Anyone know more about Firefox than i do and can point me in the right direction? I want to get it right, because it's for the .Text skin I'm writing. What I need is a Firefox equivalent of event.cancelBubble :)
Listening to: my poor brain - foo fighters - (3:33)