Capturing Mouse Events Part 2

This HTML file will demonstrate how to drag and drop HTML elements on a web page. It more formally addresses mouse events than the Path Animation Generator you created in Part 1 of this lesson. Drag and drop is a three part process - we'll need to handle the mousedown, mousemove, and mouseup events.

1. Right-click the Web Pages node in your MouseEvents application and choose New > HTML... Name the file drag and click Finish. Add the following to the <body> element:

<h1>Drag the light to the box to turn on the lights</h1>

      <div id="bulb" 
        style="left:300px; top:80px; width:100px; height:80px;"
        onmousedown="handleDown(event);"></div>
   
<div id="box" 
        style="left:300px; top:300px; width:200px; height:100px;">
        Target</div>

2. Add the following <style> element to the <head>

<style type="text/css">
      #bulb {
	position:absolute;
	z-index:200;
	color:#0000FF;
	background-color: #000000;
	background-image: url(images/lightbulb.gif);
	background-repeat: no-repeat;
	background-position: center center;
      }
    
      #box {
        position:absolute;
        background: #FFFFFF;
        color:#000000;
		border: 1px black dashed;
      }
	  body{
	  	background-color: black;
		color: white;
		font-family: sans-serif;
	  }
    </style>

Note that I've set the lighbulb to be the background image of the bulb <div>. Initially, I used a simple <img> element but ran into some problems with clicking and dragging on an image on the page. Firefox and Safari wouldn't let me drag the <img> element. An outline of the image was dragged but the location on the screen didn't change.

3. Add a <script> element in the <head> and add the following:

<script type="text/javascript">

      var offsetX, offsetY;
  
      function MouseEvent(e) 
      {
        if(e) {
          this.e = e; 
        } else {
          this.e = window.event; 
        }

        if(e.pageX) {
          this.x = e.pageX; 
        } else {
          this.x = e.clientX; 
        }

        if(e.pageY) {
          this.y = e.pageY; 
        } else {
          this.y = e.clientY; 
        }

        if(e.target) {
          this.target = e.target; 
        } else {
          this.target = e.srcElement;
        }
      }
  
      function addListener(type, callback) 
      {
        if (document.addEventListener) {
          document.addEventListener(type, callback, false);
        } else if (document.attachEvent) {
          document.attachEvent("on"+type, callback, false);
        }
      }
  
      function removeListener (type, callback) 
      {
        if (document.removeEventListener) {
          document.removeEventListener(type, callback, false);
        } else if (document.detachEvent) {
          document.detachEvent("on" + type, callback, false);
        }
      }
  
      function handleDown(e) 
      {
        var e = new MouseEvent(e);
        addListener("mousemove", handleMove);
        addListener("mouseup", handleUp);
        offsetX = e.x - parseInt(e.target.style.left);
        offsetY = e.y - parseInt(e.target.style.top);
      }
    
      function handleMove(e) 
      {
        var e = new MouseEvent(e);
        var x = e.x - offsetX;
        e.target.style.left = x + "px";
        var y = e.y - offsetY;
        e.target.style.top = y + "px";
      }
    
      function handleUp(e) 
      {
        var e = new MouseEvent(e);
        removeListener("mousemove", handleMove);
        removeListener("mouseup", handleUp);

        var target = document.getElementById("box");
        var x = parseInt(target.style.left);
        var y = parseInt(target.style.top);
        var width = parseInt(target.style.width);
        var height = parseInt(target.style.height);

        if(e.x > x && e.x < x + width &&
          e.y > y && e.y < y + height){
          var bodyElm = document.getElementsByTagName('body')[0];
		  bodyElm.style.color = 'black';
		  bodyElm.style.backgroundColor = 'white';
        }else{
			var bodyElm = document.getElementsByTagName('body')[0];
		  bodyElm.style.color = 'white';
		  bodyElm.style.backgroundColor = 'black';
		}
      }
    </script>

The offsetX and offsetY variables are calculated based on the location of the mouse within the <div> element to be dragged and the top left corner of the <div>. You've seen the MouseEvent() function and it is unchanged from Part 1.

The addListener() and removeListener() functions allow you to isolate the dragging of an element. In Part 1 of this lesson, you connected mousemove events in the <body> tag. However, all mouse move events applied to the document object. In this part of the lesson, you want to handle mouse events differently for each object (even though there is only one draggable object in this demo). addListener() and removeListener() do this for you regardless of the browser used.

You add event listeners to browsers differently. In IE, you use the built in addEventListener() function. In Firefox and other browsers, you use the attachEvent() function. To remove listeners, use removeEventListener() and detachEvent() respectively. The first argument in each specifies the event, the second specifies the code to invoke. The final argument - set to false - indicates that you want to catch events that buble up from the containing element that weren't already handled.

The handleDown() function is called in the onmousedown event of the object you want to drag. In the handleDown() function, event listeners are created for handling mousemove and mouse up events. They remain in effect until the mouseup event fires - when the dragging stops. In the handleDown() function, the event listeners are removed. So, the mousemove and mouseup events are ignored after the drop occurs. They will be re-registered if the bulb's onmousedown event fires again. The rest of the handleUp() function checks whether the bulb has actually been dropped within the box. If it has, the background and foreground colors of the <body> are swapped. Note, the conditional inside the handleUp() function is where you would tie your Ajax methods to a server call. Try the demo here.