Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme


Color

Background


Done

Table of Contents

Move Element to Click Position

by kirupa   |   filed under Coding Exercises

Learn how to calculate the exact position of a click and have an element animate to that position.

Something you see commonly in games and interactive user interfaces is this notion of some piece of UI reacting to a mouse click or finger tap. In this example, I highlight one such reaction - the famous move element to the click position.

Touch Devices Welcome

Even though the word "click" is prominent here (blame the JavaScript event of the same name), what I am about to show also works on touch devices where you tap on the screen.

The Example

Just as the name of this snippet implies, click anywhere in the gray area below to see the red smiley move to the position of your click:

There should be no surprises there! In case you want to see this example in isolation, you can view it in its own page.

Note: For An Alternate Solution

Rene Matzdorf has posted a pen that uses getClientRect that also works, so check it out here: http://codepen.io/rm89/pen/aNOmzQ

 

The Full Code

The full code for making this example work looks as follows:

<!DOCTYPE html>
<html>
 
<head>
<title>Move to Click Position</title>
<style type="text/css">
body {
	background-color: #FFF;
	margin: 30px;
	margin-top: 10px;
}
#contentContainer {
	width: 550px;
	height: 350px;
	border: 5px black solid;
	overflow: hidden;
	background-color: #F2F2F2;
	cursor: pointer;
}
#thing {
	position: relative;
	left: 50px;
	top: 50px;
	transition: left .5s ease-in, top .5s ease-in;
}
</style>
</head>

<body>
<div id="contentContainer">
	<img id="thing" src="//www.kirupa.com/images/smiley_red.png">
</div>

<script src="//www.kirupa.com/prefixfree.min.js"></script>
<script>
var theThing = document.querySelector("#thing");
var container = document.querySelector("#contentContainer");

container.addEventListener("click", getClickPosition, false);

function getClickPosition(e) {
	var parentPosition = getPosition(e.currentTarget);
	var xPosition = e.clientX - parentPosition.x - (theThing.clientWidth / 2);
	var yPosition = e.clientY - parentPosition.y - (theThing.clientHeight / 2);
	
	theThing.style.left = xPosition + "px";
	theThing.style.top = yPosition + "px";
}

// Helper function to get an element's exact position
function getPosition(el) {
  var xPos = 0;
  var yPos = 0;

  while (el) {
    if (el.tagName == "BODY") {
      // deal with browser quirks with body/window/document and page scroll
      var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
      var yScroll = el.scrollTop || document.documentElement.scrollTop;

      xPos += (el.offsetLeft - xScroll + el.clientLeft);
      yPos += (el.offsetTop - yScroll + el.clientTop);
    } else {
      // for all other non-BODY elements
      xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
      yPos += (el.offsetTop - el.scrollTop + el.clientTop);
    }

    el = el.offsetParent;
  }
  return {
    x: xPos,
    y: yPos
  };
}
</script>
</body>
</html>

As usual, take a few moments and try to understand how the example and the code that makes it up are related. After you've done that, let's walk through this example together.

How This All Works

The way this example works is pretty straightforward. The only real complication is in calculating the exact pixel value of the click location, but we'll cross that bridge when we get there. Let's look at how everything works.

Initially, you have a div with an id value of contentContainer that contains our image whose id value is thing:

<div id="contentContainer">
	<img id="thing" src="//www.kirupa.com/images/smiley_red.png">
</div>

There is nothing spectacular about this snippet of HTML, but the CSS holds some secrets - secrets that I've highlighted below:

#contentContainer {
	width: 550px;
	height: 350px;
	border: 5px black solid;
	overflow: hidden;
	background-color: #F2F2F2;
	cursor: pointer;
}
#thing {
	position: relative;
	left: 50px;
	top: 50px;
	transition: left .5s cubic-bezier(.42,-0.3,.78,1.25), 
				top .5s cubic-bezier(.42,-0.3,.78,1.25);
}

Our image's position is set to relative, and this allows us to specify the exact position using the top and left properties. Any change to either top or left is animated thanks to the two transitions defined:

#thing {
	position: relative;
	left: 50px;
	top: 50px;
	transition: left .5s cubic-bezier(.42,-0.3,.78,1.25), 
				top .5s cubic-bezier(.42,-0.3,.78,1.25);
}

You change the top and left properties when you click somewhere inside contentContainer, and that is done entirely in JavaScript.

Let's look at the JavaScript next:

var theThing = document.querySelector("#thing");
var container = document.querySelector("#contentContainer");

container.addEventListener("click", getClickPosition, false);

function getClickPosition(e) {
	var parentPosition = getPosition(container);
	var xPosition = e.clientX - parentPosition.x - (theThing.clientWidth / 2);
	var yPosition = e.clientY - parentPosition.y - (theThing.clientHeight / 2);
	
	theThing.style.left = xPosition + "px";
	theThing.style.top = yPosition + "px";
}

// Helper function to get an element's exact position
function getPosition(el) {
  var xPos = 0;
  var yPos = 0;

  while (el) {
    if (el.tagName == "BODY") {
      // deal with browser quirks with body/window/document and page scroll
      var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
      var yScroll = el.scrollTop || document.documentElement.scrollTop;

      xPos += (el.offsetLeft - xScroll + el.clientLeft);
      yPos += (el.offsetTop - yScroll + el.clientTop);
    } else {
      // for all other non-BODY elements
      xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
      yPos += (el.offsetTop - el.scrollTop + el.clientTop);
    }

    el = el.offsetParent;
  }
  return {
    x: xPos,
    y: yPos
  };
}

There are three things our JavaScript does:

  1. Setup an event listener that calls getClickPosition each time you click inside the contentContainer div element.
  2. Calculate the exact position of the click.
  3. Set our image's left and top position properties so that our transition kicks in.

We are going to focus on the third point since that is most relevant to understanding how the circle animates to the position you click. I've highlighted the relevant code for this below:

function getClickPosition(e) {
	var parentPosition = getPosition(container);
	var xPosition = e.clientX - parentPosition.x - (theThing.clientWidth / 2);
	var yPosition = e.clientY - parentPosition.y - (theThing.clientHeight / 2);
	
	theThing.style.left = xPosition + "px";
	theThing.style.top = yPosition + "px";
}

Calculating your mouse's exact click position, taking into account any offsets the parent container introduces, and factoring in the center point of the image you are trying to move isn't the simplest of things to visualize. The reason is that each of these offsets is calculated differently. Your mouse click position (e.clientX and e.clientY) is relative to your browser's top-left corner.

The position you specify inside our container isn't a global position. It needs to be normalized to the top-left position of the container itself. The following diagram should clarify what we are dealing with inside the container:

offsets visualized

The global position of the mouse click is subtracted by the parentPosition's offset and half the width and height of the image. This ensures that the center of our image stops at the exact point of our click. The final value is stored by the xPosition and yPosition variables.

The value of these variables is what you specify as the left and top properties on our image:

theThing.style.left = xPosition + "px";
theThing.style.top = yPosition + "px";

The moment the new values get assigned, our CSS transition kicks into high gear because it is just waiting for the left and top properties on our image to change.

Getting the Exact Position

The getPosition function that makes up the rest of the code is designed to get our canvas element's exact position inside our page. This is actually very important, and the reason for why we do this and how the code works can be found in the Get an Element's Position Using JavaScript tutorial.

Conclusion

One of the advantages I highlighted about CSS transitions when compared with animations is how well they work with JavaScript. When it comes to defining motion in user interfaces, there is no other relationship more valued. Interactivity where your UI reacts to events is crucial, and animations simply don't have a seat at this table.

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!

SUBSCRIBE NOW

Creating engaging and entertaining content for designers and developers since 1998.

Follow:

Popular

Loose Ends

:: Copyright KIRUPA 2024 //--