Javascript Rollover Rollout Events

Javascript has mouseover and mouseout events. Flash has these, but they also have rollover and rollout events. There is a difference, and it can be painful developing Javascript components without the rollover and rollout events. So I put together a little script that provides them for us.

Problem

You want to perform some action when the cursor rolls onto and off of an HTML element. When you use the mouseover/mouseout events, you get a mouseout and immediately another mouseover when the cursor is over a child element. Technically the mouse is still over the parent element, why does the mouseout happen? What the… you want it to work like CSS :hover. The way you’d think it SHOULD work. You figure, maybe it’s just you’re catching because bubbling child events, so you return out of your listener if the target does not equal your element. This removes the second mouseover, but you still get a mouseout when hovering over a child element.

Solution

Flash’s solution was to build in a non-bubbling rollover/rollout event that works like you’d expect. I’ve never used a mouseover/mouseout in Flash since these new events came about. So, assuming you’ve got a modern standards-compliant browser (tested on Safari/Chrome & Firefox), this little bit of code will provide rollover/rollout events for your HTML and is non-intrusive. It saves a lot of headache and works great.

Updated: The code did not previously take into account siblings, only ancestors. Fixed to find the common ancestor.

(function() {
	
	function listener(event) {
		var child = event.relatedTarget;
		var ancestor = event.target;
		// cancel if the relatedTarget is a child of the target
		while (child) {
			if (child.parentNode == ancestor) return;
			child = child.parentNode;
		}
		
		// dispatch for the child and each parentNode except the common ancestor
		ancestor = event.target.parentNode;
		var ancestors = [];
		while (ancestor) {
			ancestors.push(ancestor);
			ancestor = ancestor.parentNode;
		}
		ancestor = event.relatedTarget;
		while (ancestor) {
			if (ancestors.indexOf(ancestor) != -1) break;
			ancestor = ancestor.parentNode;
		}
		child = event.target;
		while (child) {
			var mouseEvent = document.createEvent('MouseEvents');
			mouseEvent.initEvent(event.type.replace('mouse', 'roll'),
					false, // does not bubble
					event.cancelable,
					event.view,
					event.detail, event.screenX, event.screenY,
					event.ctrlKey, event.altKey, event.metaKey, event.button,
					event.relatedTarget);
			child.dispatchEvent(mouseEvent);
			child = child.parentNode;
			if (child == ancestor) break;
		}
	}
	
	// setup the rollover/out events for components to use
	document.addEventListener('mouseover', listener, false);
	document.addEventListener('mouseout', listener, false);
})();

I hope this helps someone having a rough time with Javascript mouseovers.

2 Responses to “Javascript Rollover Rollout Events”

  1. John Says:

    I like this JS code, although I have not got it to work yet. (I’m just an HTMl guy). I was wondering about two things…
    (1) could you include this cool JS in a simple demo of HTML.. (tied to a tr) :)

    Please, if you could, give me a simple/noob definition of: (2) siblings VS. ancestors…
    maybe even title the html demo with siblings, ancestors html text. :)

    thanks

  2. Jacob Wright Says:

    Hey John, this code also only works with W3C compliant browsers. It will not work with any Internet Explorer less than 9.

    Don’t worry about ancestors and siblings. Just add an event listener to the “rollover” or “rollout” event and you’re good to go.