18

I would like to move a div with my arrow keys using jQuery. So right, left, down and up.

Found a demo of what I want to accomplish here

I would like to be able to move a div around in another div.

How can this be done?

Šime Vidas
  • 163,768
  • 59
  • 261
  • 366
Opoe
  • 1,307
  • 8
  • 28
  • 55
  • 1
    1) Create HTML and CSS with absolutely-positioned `div`. 2) Track arrow keys being pressed. 3) Change CSS `top` and `left` properties of div as appropriate. With which of of these are you having trouble? – Phrogz Feb 09 '11 at 21:31
  • Well number 2 because i dont know how i can link the arrow keys to a div – Opoe Feb 09 '11 at 21:51
  • You posted an example in your question.. with code.. what do you need from us? – Rabbott Feb 10 '11 at 01:31
  • Well i'm sorry, isn't there a shorter version? – Opoe Feb 10 '11 at 01:31
  • 2
    The example shows how to re-draw a square inside an HTML5 Canvas. Not the same as moving a div. – Phrogz Feb 10 '11 at 01:31
  • And I need some help with it, I just gave the example to show you what i want; not with a canvas but with a div moving around in another div – Opoe Feb 10 '11 at 01:31

4 Answers4

39

var pane = $('#pane'),
    box = $('#box'),
    w = pane.width() - box.width(),
    d = {},
    x = 3;

function newv(v,a,b) {
    var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
    return n < 0 ? 0 : n > w ? w : n;
}

$(window).keydown(function(e) { d[e.which] = true; });
$(window).keyup(function(e) { d[e.which] = false; });

setInterval(function() {
    box.css({
        left: function(i,v) { return newv(v, 37, 39); },
        top: function(i,v) { return newv(v, 38, 40); }
    });
}, 20);
#pane {
  position: relative;
  width: 300px;
  height: 300px;
  border: 2px solid red;
}

#box {
  position: absolute;
  top: 140px;
  left: 140px;
  width: 20px;
  height: 20px;
  background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="pane">
  <div id="box"></div>
</div>

Variable explanations:
w - the maximal left/top value that the box can have (to stay within bounds)
x - the distance (in px) that the box moves in each interval
d - this object stores the information on what key is being pressed. For instance, while the user holds down the LEFT ARROW key, d['37'] is true. Otherwise it's false. BTW, 37 is the key-code for the LEFT ARROW key and this value is stored in the e.which property of the event object. The d object is being updated on each keydown and keyup event.

An setInterval which is executed every 20ms, updates the left and top CSS properties of the box element. The new values are calculated via the newv function.

The newv function will calculate the new left/top value based on a) the old value v and b) the d object.

The expression n < 0 ? 0 : n > w ? w : n ensures that the new value is in the permitted bounds (which are 0 to w). If n is < 0, zero will be returned. If n is > w, w will be returned.


Live demo: http://jsfiddle.net/simevidas/bDMnX/1299/


Update: This code has the same functionality as the original code above. The only difference is that I used more meaningful names for my variables and arguments. As you can see, it looks awful - the original version is clearly better. :P

var pane = $('#pane'),
    box = $('#box'),
    maxValue = pane.width() - box.width(),
    keysPressed = {},
    distancePerIteration = 3;

function calculateNewValue(oldValue, keyCode1, keyCode2) {
    var newValue = parseInt(oldValue, 10)
                   - (keysPressed[keyCode1] ? distancePerIteration : 0)
                   + (keysPressed[keyCode2] ? distancePerIteration : 0);
    return newValue < 0 ? 0 : newValue > maxValue ? maxValue : newValue;
}

$(window).keydown(function(event) { keysPressed[event.which] = true; });
$(window).keyup(function(event) { keysPressed[event.which] = false; });

setInterval(function() {
    box.css({
        left: function(index ,oldValue) {
            return calculateNewValue(oldValue, 37, 39);
        },
        top: function(index, oldValue) {
            return calculateNewValue(oldValue, 38, 40);
        }
    });
}, 20);
Community
  • 1
  • 1
Šime Vidas
  • 163,768
  • 59
  • 261
  • 366
  • 5
    No disrespect intended, but despite the fact this code functions wonderfully (it's really nice), the variable names make me feel like I'm an obfuscated code competition. Reading the code is difficult because nothing has a meaningful name other than `pane` and `box` (and the stacked ternary operators don't help). But like I said, it does function really nicely. – Reid Feb 09 '11 at 23:44
  • @Reid Aren't the stacked ternary operators neat? `:)` Once you figure out what they do, it will blow your mind, hehe. But, yes, I should probably provide an explanation of my code. Give me a sec ... – Šime Vidas Feb 09 '11 at 23:56
  • @ime Vidas I upvoted you anyway, but I think using clearer variable names would be better than explaining your one character ones :) – alex Feb 10 '11 at 00:15
  • @alex I disagree. In a real-world scenario the code would be documented. I don't know if there's something wrong with my brain `:)`, but I find it easier to read code that uses one-letter variable names, compared to code with longer, more meaningful variable names. However, I've rewritten my code using your advice and posted the result in my answer. What do you think, which version is better? – Šime Vidas Feb 10 '11 at 00:50
  • @ime Vidas I much prefer the second example. :) – alex Feb 10 '11 at 01:24
7

@Šime Vidas: Your first solution is simply marvelous. (i think the second one is redundant =)

May i suggest to make two different functions for the vertical and the horizontal width? Because it’s highly unlikely that you have to move around a div inside a perfect square and i believe it would be nicer to have something like this:

$(function () {
var pane = $('#pane'),
box = $('#box'),
wh = pane.width() - box.width(),
wv = pane.height() - box.height(),
d = {},
x = 5;

function newh(v,a,b) {
    var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
    return n < 0 ? 0 : n > wh ? wh : n;
}

function newv(v,a,b) {
    var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
    return n < 0 ? 0 : n > wv ? wv : n;
}

$(window).keydown(function(e) { d[e.which] = true; });
$(window).keyup(function(e) { d[e.which] = false; });

setInterval(function() {
    box.css({
        left: function(i,v) { return newh(v, 37, 39); },
        top: function(i,v) { return newv(v, 38, 40); }
    });
}, 20);
});

This would have been exactly what i was looking for.

If you had a responsive design based on % values it would be recommendable to adjust your setInterval like this:

setInterval(function() {
    box.css({
        left: function(i,v) { return newh(v, 37, 39); },
        top: function(i,v) { return newv(v, 38, 40); }
    });
    wh = pane.width() - box.width();
    wv = pane.height() - box.height();
}, 20);

if you do that it adjusts your panes height and width and the box still stops at its border.

i made a fiddle of that here http://jsfiddle.net/infidel/JkQrR/1/

Thanks a lot.

infidel
  • 454
  • 5
  • 5
3

I can't see your demo, but here's a simple "move the box 1px in the direction of the arrow keys" example:

CSS:

#wrapper { 
    background-color: gray; 
    height:200px; 
    width: 200px; 
    position:absolute;
}
#mover { 
    background-color: white; 
    border: 1px solid red;  
    height:20px; 
    width: 20px;
    position:relative;
}

Markup:

<div id="wrapper">
    <div id="mover"></div>
</div>

JS (using jQuery):

$("#wrapper").keydown(function(event) { 
    var $mover = $("#mover");
    //if nothing else will move "mover", then track the 
    //position instead of recalculating it every time:
    //   var moverPos = $mover.position();
    //   var left = moverPos.left;
    //   var top = moverPos.top;
    var addTop = function(diff) {
        $mover.css("top", ($mover.position().top + diff) + "px"); 
        //if using tracked position:
        //   top += diff;
        //   $mover.css("top", top) + "px");
    };

    var addLeft = function(diff) {
        $mover.css("left", ($mover.position().left + diff) + "px");
        //if using tracked position:
        //   left += diff;
        //   $mover.css("left", left) + "px");
    };

    switch(event.keyCode) {
        case 37: //left
            addLeft(-1); break; 
        case 38: //up
            addTop(-1); break;
        case 39: //right
            addLeft(1); break;
        case 40: //down
            addTop(1); break;
    }
});

This is just an example, you may want to add bounds checking, larger movements, smoother animation, number pad support or any number of other things to it, but it should get you started.

jball
  • 23,602
  • 8
  • 65
  • 91
1

try This Code

<html>
<head>
     <style>
          #ss{
               background:#ccc;
               height:100px;
               width:100px;
              position: absolute;
              top: 0;
              left: 0;
             }
     </style>
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
    </script>
</head>
<body>
  <div id="ss"></div>
</body>

<script type="text/javascript">     
var $div = $('#ss');
$(document).keydown(function(e) {
     switch (e.which) {
        case 37:
              $div.css('left', $div.offset().left - 10);
              break;
        case 38:
              $div.css('top', $div.offset().top - 10);
              break;
        case 39:
              $div.css('left', $div.offset().left + 10);
              break;
        case 40:
              $div.css('top', $div.offset().top + 10);
              break;
       }
  })
 </script>

 </html>
hojjat.mi
  • 953
  • 11
  • 18