简体   繁体   中英

move selected element with arrow keys in javascript

I have a list of items, I want to be able to select an item (through click) and move only that particular item with arrow keys. I got the moving part done, but when I select second element it moves the previously selected element as well. Or if I double click the same element, it snaps it back to it's original position.

I'm guessing maybe it's because of position being relative? Though i've tried changing it to absolute and parent div as relative, still didn't work. I've tried using margins aswell, but the same problem occured.

 move(); function move(){ let list_items = document.querySelectorAll('.list-item'); for (let i = 0; i < list_items.length; i ++){ let list=list_items[i]; list.addEventListener('click',function(){ console.log(list); var objImage= null; objImage=list; objImage.style.position='relative'; objImage.style.left='0px'; objImage.style.top='0px'; function getKeyAndMove(e){ var key_code=e.which||e.keyCode; switch(key_code){ case 37: //left arrow key moveLeft(); break; case 38: //Up arrow key moveUp(); break; case 39: //right arrow key moveRight(); break; case 40: //down arrow key moveDown(); break; default: console.log(e); } } function moveLeft(){ objImage.style.left=parseInt(objImage.style.left)-5 +'px'; // objImage.style.position='static'; } function moveUp(){ objImage.style.top=parseInt(objImage.style.top)-5 +'px'; // objImage.style.position='static'; } function moveRight(){ objImage.style.left=parseInt(objImage.style.left)+5 +'px'; // objImage.style.position='static'; } function moveDown(){ objImage.style.top=parseInt(objImage.style.top)+5 +'px'; // objImage.style.position='static'; } window.addEventListener("keydown", getKeyAndMove); }); } }
 .list { display: flex; flex-flow: column; flex: 1; width: 100%; min-width: 250px; max-width: 350px; height: 100%; min-height: 150px; background-color: rgba(0, 0, 0, 0.1); margin: 0 15px; padding: 8px; transition: all 0.2s linear; }.list.list-item { background-color: #F3F3F3; border-radius: 8px; padding: 15px 20px; text-align: center; margin: 4px 0px; }
 <div class="list" id="list"> <h2>Menu Items</h2> <div class="list-item" draggable="true">List item 1</div> <div class="list-item" draggable="true">List item 2</div> <div class="list-item" draggable="true">List item 3</div> </div>

EDIT got it working by the solution proposed by @rexfordkelly. Here's the link to playground that he shared jsfiddle.net/r7ao2n5f/1

If I understand your question, and your desired behavior as:

Move one or more selected items, selections being made using a mouse, and moving performed via key presses, up, down, left and right.


I think you pretty much have it, the only thing, is your selections are never being cleared, which should be when any click occurs after a move has been made.

You may consider modeling the behavior in modes.

  • selection
  • move

Where the behavior, done in pseudo code would be something like this:

let mode = 0; // selection mode
let selections = [];

... key press event Listeners
... click event Listeners

function processAction( action, event ) {
    If ( 'click' == action && 0 != mode ){ // was in move mode
        mode = 0; // Set to selection mode.
        selections = []; // resets the selections made
        ... // reset DOM, and clear any currently decorated elements
    }

    if ( 'click' == action) {
        selections.push(event.targetElement);
        ...// Update DOM and decorate elements
    }

    If ( 'move' === action && mode !== 1) {
        mode = 1; // This enters us into "Move" mode.
    }

    If ( 'move' === action ){
        // Execute moving logic
    }
}

To address, your "double click" issue.

Or if I double click the same element, it snaps it back to it's original position

This is the result of certain "standard" behaviors, or "presets" your browser has on how it behaves, when a user drags an element. You can disable this "standard" behavior a couple ways, one is with a little CSS

.list-item  { 
  -webkit-user-drag: none; 
  -khtml-user-drag: none; 
  -moz-user-drag: none; 
  -o-user-drag: none; 
  user-drag: none; 
}
 

You can learn more about this "default" behavior on MDN here which reads:

If this attribute is not set, its default value is auto, which means drag behavior is the default browser behavior: only text selections, images, and links can be dragged...


Oh and I forgot to mention, you could simply attach your event listeners to the id="list" element, as the click events of all children will bubble up and be intercepted.

You can find details on how to figure out which element was clicked here and more about how events bubble here and on MDN here specifically the section on "Bubbling and capturing explained"

PS: I've also seen in some applications, where they have similar behavior, selections being made with the mouse, and moving with the arrow keys. They allow the user to move at a different distance, when the "shift" key is being held down, while depressing an arrow key.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM