Implementing Event Latency in JavaScript

JavaScript event latancyHandling browser events in JavaScript is probably the most time-consuming, frustrating, and misunderstood aspects of client-side programming. Libraries such as jQuery make it easier, but it’s still useful to have a little knowledge of what’s going on beneath the surface.

In this article, we will implement event latency, i.e. the response to an event will occur a little time after it fired. This is often useful in situations such as creating drop-down menus. So let’s start with a little HTML and CSS code (it’s kept brief for the purpose of this example, hence no DOCTYPE)…


<html>
<head>
<title>Latency test</title>

<style type="text/css">
#element1
{
	width: 20em;
	margin: 20px;
	background-color: #dff;
	border: 2px solid #00d;
}

#para1
{
	text-align: center;
	padding: 10px;
	margin: 30px;
	background-color: #ccc;
	border: 1px solid #000;
}
</style>

</head>
<body>

<div id="element1">
	<p id="para1">Hover over me...</p>
</div>

</body>
</html>

This just produces a box similar to the following…

Box image

Now we’ll add a little JavaScript just before the closing body tag…


<script type="text/javascript">

window.onload = function() {

	// get nodes
	var element1 = document.getElementById("element1");
	var para1 = document.getElementById("para1");

	// define events
	if (element1 && para1) {
		element1.onmouseover = Handler;
		element1.onmouseout = Handler;
	}

	// event handler
	function Handler(e) {
		e = (e ? e : window.event);
		var on = (e.type == "mouseover");
		Highlight(on);
		return false;
	}

	// highlight or dim element (pass true|false)
	function Highlight(on) {
		para1.firstChild.nodeValue = (on ? "over" : "out");
		element1.style.backgroundColor = (on ? "#ffd" : "#dff");
		if (!on) alert("moved out");
	}

};

</script>

This adds a couple of (DOM0) events that fire when the cursor moves over or out of the light-blue element1 div. An event Handler function is called to sort out IE inconsistencies and discover whether a mouse over or out event was fired. The Highlight function is passed true (mouse over) or false (mouse out) and it changes the paragraph text and background color accordingly. Finally, if the mouse is moved out, an alert box is shown.

The script works, however, when we move the mouse into the grey paragraph box the “moved out” alert appears. This occurs because the browser fires two events – a mouseout for element1 and a mouseover for para1. Although we did not delegate an event handler for para1, browsers implement a technique known as bubbling and events will propagate through all element1’s descendants.

In this example, we only care if the last event fired was a mouseout that was not immediately followed by a mouseover. We can therefore fix the problem in our Handler() function with a little event latency:


	var timer;
	function Handler(e) {
		e = (e ? e : window.event);
		var on = (e.type == "mouseover");
		if (timer) clearTimeout(timer);
		timer = setTimeout( function() { Highlight(on); }, 300);
		return false;
	}

When an event occurs, we clear any existing timeout. A new timeout is then created which calls the Highlight function after 300 milliseconds (Highlight(on) is contained in a function so the value of ‘on’ is retained by the closure). This process makes it impossible for Highlight() to be executed any more frequently than every 300 milliseconds. When it is called, only the most recent value of ‘on’ is available.

Reload the page and you will notice that there is a short delay before any event animation occurs. In addition, the alert will only appear when you move outside of the blue #element1 box.

I hope you find the technique useful in other JavaScript projects.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.dangrossman.info Dan Grossman

    Good topic to cover, thanks. I recently implemented a solution like this for some dropdown menus. Users sometimes have trouble keeping the mouse pointer over the menu elements while moving it to choose one, and you don’t want the menu to collapse itself immediately when the pointer leaves for just a fraction of a second. So the menu is on a delay similar to your code that watches for the mouse returning to the menu within X milliseconds to cancel the collapse.

  • http://www.brothercake.com/ brothercake

    There’s a couple of things wrong with this.

    Firstly – the issue you mentioned of mouseout and mouseover events cross-firing between the two elements is far better fixed by discriminating the event target using a contains() method:

    object.contains = function(node)
    {
    if(node == this) { return true; }
    if(nodes == null) { return false; }
    else { return this.contains(node.parentNode); }
    }

    Using a timer to fix this issue is really just pasting over it, and not dealing with it at all – if you rely on such a solution, I guarantee it will come back and bite you later on.

    Secondly – you say that a timer is useful for dropdown menus, but it’s only useful if it’s discriminated in some way. You need to ensure that the event is still inside the target element at the end of the timer, otherwise all you’ve done is introduce a useless pause.

  • http://www.brothercake.com/ brothercake

    Sorry, here’s that method again (free of typos this time!)

    object.contains = function(node)
    {
      if(node == this) { return true; }
      if(node == null) { return false; }
      else { return this.contains(node.parentNode); }
    };

  • http://www.optimalworks.net/ Craig Buckler

    Thanks for the feedback. The code above was a quick example showing how event latency can be implemented with setTimeout. It’s certainly not a solution for cross-firing issues, so apologies if that’s not clear enough.

  • http://www.brothercake.com/ brothercake

    What did you intend it as a solution for?

  • http://goddes-dubai.blogspot.com Derekp

    I think i’ve seen this somewhere before…but it’s not bad at all

  • http://www.calcResult.co.uk omnicity

    How about doing a re-write then, using a real example?

    As it stands, the article doesn’t really match the title.