CS Ed Week
© 13 Dec 2012 Luther Tychonievich
Licensed under Creative Commons: CC BY-NC-ND 3.0
other posts

Making things move.

 

In writing these posts I have no idea how fast to go. Perhaps the previous post was too much—it was more than is typically done the second day of a programming class, anyway. But on the hope it was not too much, here I go doing more.

Today I’m going to show how to make a piece of text flee from the pointer. This will require us to delve into the mysteries of the DOM. For example, the p element we used last time has no left or right edges (filling, by definition, the width of the document), so we’ll use the span element instead. Also, we’ll bump into the annoying way that different browsers handling the same functionality in different ways.

If we make a span element like s = document.createElement("span"), put some text in it with s.innerHTML = "something", and add it to the document with document.body.appendChild(s) we will see text appears near the top left of the document. Well, actually of the document’s body since that’s the part of the document that has content; the title, URL, etc, are also part of a document. It goes there because the default way to position a span element is to go left-to-right, top-to-bottom, after the most recent element added.

We can change this default by messing with the style of the element. Conceptually, the style stores information about how an element looks while the element itself contains information about what it contains. One of the things inside each element’s style is it’s position. s.style.position is a string describing how, not where, the object is placed. We’ll replace it with s.style.position = "relative" meaning it should be positioned relative to the top right corner of the document body instead of immediately after the previously added element. We’ll then change it’s location with s.style.left = 100 and s.style.top = 100, stating how many pixels the span should be from the left and top edges of the document, respectively. We now have s = document.createElement("span")
s.innerHTML = "calm"
document.body.appendChild(s)
s.style.position = "relative"
s.style.left = 100
s.style.top = 100
which places the word “‍calm‍” 100 pixels from the top left corner of the document.

We now bump into one of the strangenesses of the DOM. If we look at the top we just set to 100 by doing alert(s.style.top) we see not the number “‍100‍” but the string “‍100px‍” At least, we do in some browsers. Not all web browsers do this the same way. . We can’t do math on a string like "100px" and we’ll want to do math on position to move the text so we’ll need to store the numbers too. Fortunately, since s is an object, all we need to do is add a couple extra keys to it: s.posX = 100
s.posY = 100
We could have picked anything instead of posX and posY; anytime we create a name it is completely our choice what it is. Since we defined these keys ourselves, we know that the browser won’t do any tricky changing them into strings or the like; it’ll just store the numbers directly. We can verify this by an alert(s.posX).

The next thing to do is figure out where the cursor is located. We do this by adding an “‍event handler‍” to the document. These are functions that are invoked in special situations. One of them, onmousemove is invoked anytime the cursor moves. The value of onmousemove needs to be a function, so we write the following: document.body.onmousemove = function(e) {
  if (!e) { e = window.event }
  mouseX = e.pageX
  mouseY = e.pageY
}
The first line of this method is a bit strange. The !e is true if e is not defined. Some browsers give information about the event as a parameter to the onmousemove function and others store it inside a special window object. The if statement says “‍if we didn’t get it as a parameter then go grab it from the window object.‍” The other two lines just look up the position of the mouse from the event object.

Now we need to decide what to do when the mouse moves inside the span. The simplest thing to do is move the span away from the mouse.

First figure out how far the mouse is from each edge of the span, and then change the span’s position so the smallest of those is zero. The distance from the top is simply the difference between the mouse position and the span’s position. The difference from the bottom is the difference between the mouse position and the sum of the span position and the span height. We also do the subtraction such that larger mouseY make fromTop bigger and fromBottom smaller. fromTop = mouseY - s.posY
fromBottom = (s.posY + s.offsetHeight) - mouseY
A similar computation works for the left and right fromLeft = mouseX - s.posX
fromRight = (s.posX + s.offsetWidth) - mouseX

Now we’ll figure out which edge is closest to the mouse. There is a built-in function called Math.min that will help: smallest = Math.min(fromTop, fromBottom, fromLeft, fromRight)
if (smallest <= 0) { return }
if (fromTop == smallest) { s.posY = mouseY }
if (fromLeft == smallest) { s.posX = mouseX }
if (fromBottom == smallest) { s.posY = mouseY - s.offsetHeight }
if (fromRight == smallest) { s.posX = mouseX - s.offsetWidth }
The second line has a new construction. The return here says “‍stop this function and ignore what follows‍” so we’ll only keep going if the smallest distance is positive. We then figure out which distance is smallest and move the numbers we are storing inside span accordingly. Notice the use of == to compare values; since = changes a value we need to use something different to check for equality; == is operator used by Javascript.

Finally, we update the actual position of the span using s.style.left and s.style.top. The final function looks like document.body.onmousemove = function(e) {
  if (!e) { e = window.event }
  mouseX = e.pageX
  mouseY = e.pageY
  fromTop = mouseY - s.posY
  fromBottom = (s.posY + s.offsetHeight) - mouseY
  fromLeft = mouseX - s.posX
  fromRight = (s.posX + s.offsetWidth) - mouseX
  smallest = Math.min(fromTop, fromBottom, fromLeft, fromRight)
  if (smallest <= 0) { return }
  if (fromTop == smallest) { s.posY = mouseY }
  if (fromLeft == smallest) { s.posX = mouseX }
  if (fromBottom == smallest) { s.posY = mouseY - s.offsetHeight }
  if (fromRight == smallest) { s.posX = mouseX - s.offsetWidth }
  s.style.left = s.posX
  s.style.top = s.posY
}
That’s it! Assuming you have a standards-compliant browser (i.e., not Internet Explorer Internet Explorer, for whatever reason, uses a different DOM than other browsers and each version of Internet Explorer uses a different DOM than each other version. Most other browsers (SRWare Iron, Midori, Konqueror, Opera, Firefox, K-Meleon, Slepnir, SeaMonkey, Camino, Chromium, Safari, etc) don’t have this problem. ), the span will now flee from the mouse.




Looking for comments…



Loading user comment form…