O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

7.2. Capturing the Location of a Mouse Click Event Using the Event Object

Problem

You need to discover the location in the web page where a mouse click occurred.

Solution

Assign the onclick event handler to the document object, and when the event handler function is processed, access the click location from the Event object:

document.onclick=processClick;
...
function processClick(evt) {

   // access event object
   evt = evt || window.event;
   var x = 0; var y = 0;

   // if event object has pageX property
   // get position using pageX, pageY
   if (evt.pageX) {
      x = evt.pageX;
      y = evt.pageY;

    // else if event has clientX property
   } else if (evt.clientX) {
      var offsetX = 0; offsetY = 0;

       // if documentElement.scrollLeft supported
      if (document.documentElement.scrollLeft) {
          offsetX = document.documentElement.scrollLeft;
          offsetY = document.documentElement.scrollTop;
      } else if (document.body) {
          offsetX = document.body.scrollLeft;
          offsetY = document.body.scrollTop;
      }

      x = evt.clientX + offsetX;
      y = evt.clientY + offsetY;
    }

alert ("you clicked at x=" + x + " y=" + y);
}

Discussion

Whoa! We didn’t expect all of this for a simple task such as finding the location in the page of a mouse click. Unfortunately, this recipe is a good demonstration of the cross-browser challenges associated with JavaScript event handling.

From the top: first, we need to find information about the event from the event object. The event object contains information specific to the event, such as what element received the event, the given location relative to the event, the key pressed for a keypress event, and so on.

In the solution, we immediately run into a cross-browser difference in how we access this object. In IE8 and earlier, the Event object is accessible from the Window object; in other browsers, such as Firefox, Opera, Chrome, and Safari, the Event object is passed, by default, as a parameter to the event handler function.

The way to handle most cross-browser object differences is to use a conditional OR (||) operator, which tests to see if the object is not null. In the case of the event object, the function argument is tested. If it is null, then window.event is assigned to the variable. If it isn’t, then it’s reassigned to itself:

evt = evt || window.event;

You’ll also see another approach that’s not uncommon, and uses the ternary operator, to test to see if the argument has been defined and is not null. If it isn’t, the argument is assigned back to the argument variable. If it is, the window.event object is accessed and assigned to the same argument variable:

evt = evt ? evt : window.event;

Once we’ve worked through the event object difference, we’re on to the next. Firefox, Opera, Chrome, and Safari both get the mouse location in the web page via the nonstandard event pageX and pageY properties. However, IE8 doesn’t support these properties. Instead, your code can access the clientX and clientY properties, but it can’t use them as is. You have to adjust the value to account for any offset value, due to the window being scrolled.

Again, to find this offset, you have to account for differences, primarily because of different versions of IE accessing your site. Now, we could consider disregarding anything older than IE6, and that’s an option. For the moment, though, I’ll show support for versions of IE both older and newer than IE6.

For IE6 strict and up, you’ll use document.documentElement.scrollLeft and document.documentElement.scrollTop. For older versions, you’ll use document.body.scrollLeft and document.body.scrollTop. Here’s an example of using conditional statements:

var offsetX = 0; offsetY = 0;
if (document.documentElement.scrollLeft) {
   offsetX = document.documentElement.scrollLeft;
   offsetY = document.documentElement.scrollTop;
} else if (document.body) {
   offsetX = document.body.scrollLeft;
   offsetY = document.body.scrollTop;
}

Once you have the horizontal and vertical offsets, add them to the clientX and clientY values, respectively. You’ll then have the same value as what you had with pageX and pageY:

x = evt.clientX + offsetX;
y = evt.clientY + offsetY;

To see how all of this holds together, Example 7-1 shows a web page with a red box. When you click anywhere in the web page, the box is moved so that the top-left corner of the box is positioned exactly where you clicked. The example implements all of the various cross-browser workarounds, and operates safely in all the main browser types, even various older browsers.

Example 7-1. Capturing the mouse-click location and moving element to location

<!DOCTYPE html>
<head>
<title>Box Click Box Move</title>
<style type="text/css">

#info
{
  width: 100px; height: 100px;
  background-color: #ff0000;
  position: absolute;
  top: 0;
  left: 0;
}
</style>
<script>

window.onload=function() {
  document.onclick=processClick;
}

function processClick(evt) {
  evt = evt || window.event;
  var x = 0; var y = 0;
  if (evt.pageX) {
    x = evt.pageX;
    y = evt.pageY;
  } else if (evt.clientX) {
    var offsetX = 0; offsetY = 0;
    if (document.documentElement.scrollLeft) {
       offsetX = document.documentElement.scrollLeft;
       offsetY = document.documentElement.scrollTop;
    } else if (document.body) {
       offsetX = document.body.scrollLeft;
       offsetY = document.body.scrollTop;
    }

    x = evt.clientX + offsetX;
    y = evt.clientY + offsetY;
  }

  var style = "left: " + x + "px; top: " + y + "px";
  var box = document.getElementById("info");
  box.setAttribute("style", style);
}
</script>
</head>
<body>
<div id="info"></div>
</body>

Cross-browser differences may seem overwhelming at times, but most of the code can be packaged into a library as a reusable event handler. However, there is a good likelihood that many of these browser differences will vanish with IE9.

Note

The example doesn’t work with IE7 because of the use of setAttribute with the style attribute. The downloaded example contains a workaround.

See Also

Mozilla has, as usual, excellent documentation on the event object at https://developer.mozilla.org/En/DOM/Event. See Recipe 12.15 for a description of setAttribute.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required