-1

I'm trying to get further with js, so I've been looking for a simple and as short script as a possiblity to move a letter, dot, (whatever) to specific coordinates or just to draw it at those coordinates. I also would like an arrow key based movement.

I've been looking at the tetris game, but it's quite a lot of code even if you clean a lot of it up.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140

5 Answers5

7

You've said you want to put a box or something on the page and move it around with arrow keys. So that breaks down into:

  1. Having an element to put on the page
  2. Being able to access that element
  3. Being able to set its position
  4. Capturing keyboard events

1 - Having an element to put on the page

You can position an element on the page with full control over its location by making it an "absolutely positioned" element: Settings its position style property to absolute:

<div style="position: absolute;"></div>

or better

<style>
#target {
    position: absolute;
}
</style>

<div id="target"></div>

For that div to actually appear, you want to give it some size and probably some visible aspect:

<style>
#target {
    position:         absolute;
    width:            10px; /* Or whatever */
    height:           10px; /* Or whatever */
    border:           1px solid black;
    background-color: #ddd;
}
</style>

2 - Being able to access the element

Above we gave the element an id. That means we can look it up using document.getElementById. This isn't the only way to access an element, but it's simple and straightforward:

var element;
element = document.getElementById("target");

3 - Being able to set its position

This is done with the left, top, right, and bottom properties, which you can set via the style property on the element instance. Let's put it 10 pixels from the top of the page and 50 pixels from the left:

element.style.top = "10px";
element.style.left = "50px";

Note that style values are always strings, and you must include the unit, just like in a stylesheet.

4 - Capturing keyboard events

For arrow keys, your best bet is the keydown event, since it fires for non-printable keys (like arrows) and repeats if the key is held down. Here we get into one of the many places where a good library like jQuery, Prototype, YUI, Closure, or any of several others can help you, because the best way to hook up an event handler is to use the DOM2 style of handler, but although Microsoft created them, when they were standardized the name and parameters were different from Microsoft's and Microsoft took a long time to support the standard. So you have to handle both Microsoft's way (attachEvent) and the standard way (addEventListener). Any good library provides a simple, unified way to hook up event handlers.

This answer has a cross-browser hookEvent function we can use for this example (full source for it is in the final example at the bottom of this answer as well). We'd use it like this:

hookEvent(document, "keydown", function(event) {
    // This function gets called when there's a keydown event

    // If we want to prevent the event from propagating (bubbling),
    // we can use `event.stopPropagation();` here. Similarly, if
    // we want to prevent any default action, we can use
    // `event.preventDefault();` here
});

To find out what key was pressed, we use keyCode or which — another cross-browser hassle that any decent library would handle for you. So:

hookEvent(document, "keydown", function(event) {
    var key = event.which || event.keyCode;
});

That sets key to event.which if it isn't falsey, and event.keyCode if which is falsey (via JavaScript's Curiously Powerful || operator). All that's left is to handle the keys and move the element:

hookEvent(document, "keydown", function(event) {
    var element, left, top;

    element = document.getElementById("target");
    left = parseInt(element.style.left, 10);
    top  = parseInt(element.style.top, 10);
    switch (event.which || event.keyCode) {
        case 37: // Left
            left = Math.max(0, left - 10);
            break;
        case 39: // Right
            left += 10;
            break;
        case 38: // Up
            top = Math.max(0, top - 10);
            break;
        case 40: // Down
            top += 10;
            break;
    }
    element.style.left = left + "px";
    element.style.top  = top  + "px";

    // Stop propagation and prevent default
    event.stopPropagation();
    event.preventDefault();
});

There we're re-getting the element every time and we're parsing the existing left and top values out of the style property, which is fine as keyboard events don't happen all that often, but isn't necessary. Ideally you want to put all your code inside a "scoping function" so you don't create global symbols, and if we do that we can just get the element once and then use it in the event handler because the event handler we'll create will be a closure over the context of the scoping function (don't worry, closures are not complicated). Similarly we can use local variables within that scoping function to track left and top. So:

(function() {
    var element = document.getElementById("target"),
        left = 0,
        top = 0;

    element.style.left = left + "px";
    element.style.top  = top  + "px";

    hookEvent(document, "keydown", function(event) {
        switch (event.which || event.keyCode) {
            case 37: // Left
                left = Math.max(0, left - 10);
                break;
            case 39: // Right
                left += 10;
                break;
            case 38: // Up
                top = Math.max(0, top - 10);
                break;
            case 40: // Down
                top += 10;
                break;
        }
        element.style.left = left + "px";
        element.style.top  = top  + "px";

        // Stop propagation and prevent default
        event.stopPropagation();
        event.preventDefault();
    });
})();

This code must be after the <div id="target"></div> on the page, because it assumes that the element is already there.

Bringing that all together

Runnable live example *(you have to click the box to make sure that frame has focus before the keys will work):

(function() {
  // A cross-browser hook event function, tucked at the end to keep
  // it out of the way
  var hookEvent = makeHookEventFunction();

  // Get the element, and our initial position
  var element = document.getElementById("target"),
    left = 0,
    top = 0;

  // Set the initial position on the element
  element.style.left = left + "px";
  element.style.top = top + "px";

  // Handle the 'keydown' event
  hookEvent(document, "keydown", function(event) {
    // Work with 'which' or 'keyCode', whichever the browser gives us
    switch (event.which || event.keyCode) {
      case 37: // Left
        left = Math.max(0, left - 10);
        break;
      case 39: // Right
        left += 10;
        break;
      case 38: // Up
        top = Math.max(0, top - 10);
        break;
      case 40: // Down
        top += 10;
        break;
      default:
        // Not for us
        return;
    }
    
    // Update the element's position
    element.style.left = left + "px";
    element.style.top = top + "px";
    
    // Since we've handled the key event, prevent it from
    // propagating and prevent its default action
    event.stopPropagation();
    event.preventDefault();
  });
  
  // ------------------------------------------------------------

  // A cross-browser "hook event" function, see my answer at
  // https://stackoverflow.com/questions/23799296/js-li-tag-onclick-not-working-on-ie8/23799448#23799448
  // for details.
  function makeHookEventFunction() {
    var div;

    // The function we use on standard-compliant browsers
    function standardHookEvent(element, eventName, handler) {
      element.addEventListener(eventName, handler, false);
      return element;
    }

    // The function we use on browsers with the previous Microsoft-specific mechanism
    function oldIEHookEvent(element, eventName, handler) {
      element.attachEvent("on" + eventName, function(e) {
        e = e || window.event;
        e.preventDefault = oldIEPreventDefault;
        e.stopPropagation = oldIEStopPropagation;
        handler.call(element, e);
      });
      return element;
    }

    // Polyfill for preventDefault on old IE
    function oldIEPreventDefault() {
      this.returnValue = false;
    }

    // Polyfill for stopPropagation on old IE
    function oldIEStopPropagation() {
      this.cancelBubble = true;
    }

    // Return the appropriate function; we don't rely on document.body
    // here just in case someone wants to use this within the head
    div = document.createElement('div');
    if (div.addEventListener) {
      div = undefined;
      return standardHookEvent;
    }
    if (div.attachEvent) {
      div = undefined;
      return oldIEHookEvent;
    }
    throw "Neither modern event mechanism (addEventListener nor attachEvent) is supported by this browser.";
  }

})();
#target {
  position: absolute;
  width: 10px;
  /* Or whatever */
  height: 10px;
  /* Or whatever */
  border: 1px solid black;
  background-color: #ddd;
}
<div id="target"></div>

Some references:

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • yeah, well, If you're going to write his program for him, go ahead. – Rodik Jan 02 '12 at 15:23
  • @Rodik: My goal with the above was to teach him/her to fish. Granted there was some giving him fish in the process, but that's the nature of teaching. :-) Since the OP said he/she was just getting started with this stuff, I thought a breakdown of how you do it, an example, and some references would be appropriate. – T.J. Crowder Jan 02 '12 at 16:49
  • Excellent response, you've got my upvote. Just a few bits are bugging me though. First off, you really don't need to re-parse the "left" and "top" at every frame. Second, though it doesn't matter in this example where it's only called once, hookEvent doesn't have to re-test browser support every time it's called (also, `typeof` doesn't add anything to simply testing if it's falsy). And finally, you can keep a reference to the element's style instead of the entire element. But those are all just optimizations - very imformative code nonetheless. –  Jan 02 '12 at 18:36
  • @Crowder: ahh I wasn't really saying that in a bad way. that was a joke mostly. very nice and thorough answer, though. – Rodik Jan 02 '12 at 21:00
  • @skier88: Yeah, you could use the closure to track `left` and `top`. I didn't provide the one-time-test version of `hookEvent` because I wanted to keep it simple. Besides, the cost of re-checking each time is completely undetectable. You only have to micro-optimize things like that if you're calling them hundreds of thousands of times in a tight loop, which of course you wouldn't be here. – T.J. Crowder Jan 02 '12 at 21:37
  • Heh, yeah, I do have a tendency to over-optimize. That being said, I don't think that storing left and top as integers in the closure is excessive, since it is called in a performance-critical function and it also makes the code shorter and more readable. Going along the lines of "teaching to fish", it's easy to get in the habit of discarding information you're going to need later, and eventually you'll regret it. –  Jan 02 '12 at 22:00
  • @skier88: Yeah, I'm pretty sure if I were doing this in production code, I'd track `left` and `top` separately. – T.J. Crowder Jan 03 '12 at 09:53
  • @skier88: And if I think I'd do it in production code, then I really should have done it here. So I have, thanks. – T.J. Crowder Jan 03 '12 at 11:19
1

A good idea would be to start with an absolutely positioned element.

<div id="moveme" style="position:absolute;">X</div>

you can then use jQuery (or any other framework) to move it using the left and top css properties as coordinates. example:

$('#moveme').animate({left: "100px", top: "240px"});

or if you do not wish to have sliding animations, just use:

 $('#moveme').css({left: "100px", top: "240px"});

or if you do not wish to use jQuery (for some freaky reason):

document.getElementById("moveme").style.top="50px";

source: http://api.jquery.com/animate/

To move the div:

function move(e){
    alert(e.keyCode);
}
document.onkeydown = move;

use e.keyCode to see where you need to move your thingy.

Rodik
  • 4,054
  • 26
  • 49
  • you can use this instead of jquery document.getElementById("moveme").style.top="50px"; – Rodik Jan 02 '12 at 11:27
  • thanks a lot! that realy helped! now how could i use the arrow keys to move? Right now I used to make it folow the mouse, but I'd like also to move with arrow keys. –  Jan 02 '12 at 11:37
  • I changed onkeypress to onkeydown because it supports the arrow keys as well as the rest of the keys. – Rodik Jan 02 '12 at 11:49
0

jQuery is massive overkill. To answer your question, the javascript would be simply:

el.style.left=xPos+'px';

Of course, that's out of context. So here's an example.

<!doctype html>
<html>
  <head>
    <meta charset='utf-8'>
    <title>js test</title>
    <style type='text/css'>
      *   {margin:0; padding:0;}
      div {position:absolute; width:100px; height:100px; background:red;}
    </style>
  </head>
  <body>
    <div></div>
    <script type='text/javascript'>
      var el=document.getElementsByTagName('div')[0];
      setTimeout(function() {
        el.style.left='100px';
      },2000);
    </script>
  </body>
</html>

The timeout is optional; it's there purely to demonstrate that the javascript is in fact moving the element. Other than that, there are a few things to note:

  1. The div is styled position:absolute. Besides making it easy to animate, this makes it far more efficient on more complex pages, since the targeted element's position doesn't affect the other elements on the page.
  2. You need to specify units. In this example I used 'px' - pixels.
  3. This is not applicable to this example, but if you are changing multiple values in the same frame then change them at the same time by setting el.style.cssText. If you don't do this then the browser may end up redrawing more than you want it to.

To move the elements with arrow keys you would need to attach callbacks to the window.onkeydown event, like this.

window.addEventListener('keydown',function(e) {
  if(e.which==39) {
    xPos++;
    el.style.left=xPos+'px';
  }
});
0

This is alternative to the other suggestions, in that it doesn't animate HTML elements:

Use the canvas element. It allows you do draw shapes and forms on specific coordinates, in a very simple way. There are may libraries that simplify the API, but you should get far with just the basics.

var canvas = document.getElementById("canvas");  
if (canvas.getContext) {  
    var ctx = canvas.getContext("2d");  

    ctx.fillStyle = "rgb(200,0,0)";  
    ctx.fillRect (10, 10, 55, 50);
}
Emil Stenström
  • 13,329
  • 8
  • 53
  • 75
0

Good idea is to get familiar with JavaScript first.

If you have JVM(Java Virtual Machine), there comes a JavaScript interpreter with it. Just run the command

jrunscript

in command line or terminal.

Write few JavaScript sample programs such as -

  1. Factorial (with and without recursion)
  2. JavaScript Inheritance

Then move to how JS is useful in browsers. Read Wikipedia article about DOM. Google "how a browser loads a web page". Then start writing HTML page with script tag in it. You can write few sample programs such as-

  1. alert something (thats pleasing)
  2. Draw some elements such as textboxes and buttons.
  3. take jquery tutorial.

Hardly 2 hours will be required to do all these steps. Then you will be able to design Tetris on your own. All the best.

hrishikeshp19
  • 8,838
  • 26
  • 78
  • 141