JavaScript Как динамически перемещать Div, щелкая и перетаскивая

Хорошо, казалось бы, это должно быть просто. Мне нужно взять уже существующий div и переместить его в соответствии с положением мыши в окне. Я искал везде, и это привело меня к слишком сложным способам выполнения одного и того же действия, включая использование j-query. Мне нужно строго использовать javascript для того, что я пытаюсь сделать.

Метод:

var mousePosition;
var div;

(function createDiv(){

    div = document.createElement("div");
    div.style.position = "absolute";
    div.style.left = "0px";
    div.style.top = "0px";
    div.style.width = "100px";
    div.style.height = "100px";
    div.style.background = "red";
    div.style.color = "blue";

    div.addEventListener('mousedown', handleKeyPressed, true);

    document.body.appendChild(div);


})();

function handleKeyPressed(event) {

    event.preventDefault();

    mousePosition = {

        x : event.clientX,
        y : event.clientY

    };

    div.style.left = mousePosition.x;
    div.style.top = mousePosition.y;

    //alert("whoa!");

}

person StoneAgeCoder    schedule 05.06.2014    source источник
comment
jQuery может это сделать, он отлично подходит для всего. Добавьте пользовательский интерфейс jQuery, и у вас есть возможность перетаскивания из коробки.   -  person adeneo    schedule 05.06.2014
comment
@adeneo jQuery - это библиотека javascript, которую я хочу узнать о javascript, а не о jquery, хотя я знаю, что ее обычно используют, и на данный момент не заинтересован в ее изучении. Просто смотрю на чистый javascript.   -  person StoneAgeCoder    schedule 05.06.2014


Ответы (7)


Я думаю, вы ищете что-то более похожее на это

var mousePosition;
var offset = [0,0];
var div;
var isDown = false;

div = document.createElement("div");
div.style.position = "absolute";
div.style.left = "0px";
div.style.top = "0px";
div.style.width = "100px";
div.style.height = "100px";
div.style.background = "red";
div.style.color = "blue";

document.body.appendChild(div);

div.addEventListener('mousedown', function(e) {
    isDown = true;
    offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

document.addEventListener('mouseup', function() {
    isDown = false;
}, true);

document.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (isDown) {
        mousePosition = {

            x : event.clientX,
            y : event.clientY

        };
        div.style.left = (mousePosition.x + offset[0]) + 'px';
        div.style.top  = (mousePosition.y + offset[1]) + 'px';
    }
}, true);

FIDDLE

person adeneo    schedule 05.06.2014
comment
Хотя это немного нервно. - person Ali Gajani; 05.06.2014
comment
Это потому, что он всегда находится в верхнем левом углу указателя мыши, вам нужно вычислить смещение элемента относительно того места, где вы щелкнули, но это почти вопрос сам по себе. - person adeneo; 05.06.2014
comment
@аденео спасибо. :) хотя вы могли бы просто назвать меня идиотом за то, что я забыл добавить px в конец div.style.left = e.clientX;. Но это помогло мне еще больше ценится. - person StoneAgeCoder; 05.06.2014
comment
@StoneAgeCoder - Да, в простых JS требуются модули. Посмотрим, сможем ли мы исправить это дрожание, одну секунду. - person adeneo; 05.06.2014
comment
@аденео одно слово. идеально. - person StoneAgeCoder; 05.06.2014
comment
@StoneAgeCoder - спасибо, надеюсь, это сработает для того, что вы пытаетесь сделать! - person adeneo; 05.06.2014
comment
Это, безусловно, лучшее решение, которое я когда-либо видел. Скрипт небольшой и простой для понимания, а результаты работают лучше, чем что-либо еще, что я видел в различных браузерах. Я вынужден комментировать и голосовать, хотя бы для того, чтобы найти это снова в будущем. - person sanepete; 17.08.2018
comment
Отличное решение, но вы можете везде использовать div.addEventListener вместо document.addEventListener, если хотите отпустить мышь для других действий с документом. Спасибо - person Konstantin; 27.02.2020
comment
Поместите event.preventDefault(); внутри if (isDown), иначе вы не сможете взаимодействовать со страницей. - person Paul; 12.10.2020
comment
мой элемент не движется должным образом в направлении x, но нормально движется в направлении y, что может быть изношено? @adeneo Я перемещаю существующий элемент вместо создания нового элемента. - person AbhimanuSharma; 08.12.2020
comment
При быстром перемещении это может вызвать некоторые сбои рендеринга. Я решил это, обернув изменение позиции элемента вызовом window.requestAnimationFrame(). - person Ken Wayne VanderLinde; 16.12.2020
comment
Большое спасибо за эту демонстрацию перетаскиваемых элементов div! Я сделал улучшенную версию здесь: jsfiddle.net/gcakuw50/2 - person Ryan; 14.04.2021
comment
stackoverflow.com/a/25933582/470749 тоже хорош. - person Ryan; 14.04.2021
comment
@adeneo Вы спаситель, сэр! Большое спасибо, я пытался решить эту проблему в течение нескольких дней самостоятельно. - person M. Çağlar TUFAN; 28.04.2021

Поддержка сенсорного ввода

Все остальные ответы (включая принятый) не работают с сенсорным вводом. Сенсорный ввод имеет события, отличные от событий ввода с помощью мыши. См. Использование сенсорных событий на MDN.

Следующий фрагмент кода работает даже при сенсорном вводе. Я выделил все строки кода, которые необходимо добавить для поддержки сенсорных устройств.
Основная идея заключается в том, что каждый элемент, содержащий draggable в списке классов, должен быть перетаскиваемым. Этой концепции легче следовать, когда у вас есть большое количество элементов, которые нужно перетаскивать.

См. эту страницу Glitch и последующие для демонстрации.

const d = document.getElementsByClassName("draggable");

for (let i = 0; i < d.length; i++) {
  d[i].style.position = "relative";
}

function filter(e) {
  let target = e.target;

  if (!target.classList.contains("draggable")) {
    return;
  }

  target.moving = true;
  
//NOTICE THIS ????
  e.clientX ? // Check if Mouse events exist on user' device
  (target.oldX = e.clientX, // If they exist then use Mouse input
  target.oldY = e.clientY) :
  (target.oldX = e.touches[0].clientX, // otherwise use touch input
  target.oldY = e.touches[0].clientY)
//NOTICE THIS ???? Since there can be multiple touches, you need to mention which touch to look for, we are using the first touch only in this case

  target.oldLeft = window.getComputedStyle(target).getPropertyValue('left').split('px')[0] * 1;
  target.oldTop = window.getComputedStyle(target).getPropertyValue('top').split('px')[0] * 1;

  document.onmousemove = dr;
//NOTICE THIS ????
  document.ontouchmove = dr;
//NOTICE THIS ????

  function dr(event) {
    event.preventDefault();

    if (!target.moving) {
      return;
    }
//NOTICE THIS ????
    event.clientX ?
    (target.distX = event.clientX - target.oldX,
    target.distY = event.clientY - target.oldY) :
    (target.distX = event.touches[0].clientX - target.oldX,
    target.distY = event.touches[0].clientY - target.oldY)
//NOTICE THIS ????

    target.style.left = target.oldLeft + target.distX + "px";
    target.style.top = target.oldTop + target.distY + "px";
  }

  function endDrag() {
    target.moving = false;
  }
  target.onmouseup = endDrag;
//NOTICE THIS ????
  target.ontouchend = endDrag;
//NOTICE THIS ????
}
document.onmousedown = filter;
//NOTICE THIS ????
document.ontouchstart = filter;
//NOTICE THIS ????
.draggable {
  width: 100px;
  height: 100px;
  background: red;
}
<div class="draggable"></div>

person mmaismma    schedule 15.08.2020

Проверьте, работает ли он более плавно, чем adeneo: FIDDLE

var m = document.getElementById('move');
m.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);

function mouseUp() {
    window.removeEventListener('mousemove', move, true);
}

function mouseDown(e) {
    window.addEventListener('mousemove', move, true);
}

function move(e) {
    m.style.top = e.clientY + 'px';
    m.style.left = e.clientX + 'px';
};
person jhyap    schedule 05.06.2014
comment
Я не нашел это более гладким, чем принятый ответ. На самом деле это было немного нервно. Я делаю что-то неправильно? - person sanepete; 17.08.2018
comment
Ага. принятый ответ теперь более гладкий, чем мой. Пожалуйста, используйте принятый ответ. - person jhyap; 20.08.2018

Я только что внес небольшое изменение в очень хорошо работающий ответ @adeneo. Если все заключено в функцию, и каждое событие прикреплено к div, вы можете использовать его как часть библиотеки.

Вызовите следующую функцию, передав идентификатор. Если div не существует, он создается.

function drag_div(div_id){
var div;

div = document.getElementById(div_id);

if(div == null){
   div = document.createElement("div");
   div.id = div_id;
   div.style.position = "absolute";
   div.style.left = "0px";
   div.style.top = "0px";
   div.style.width = "100px";
   div.style.height = "100px";
   div.style.background = "red";
   div.style.color = "blue";
   document.body.appendChild(div);
}

div.addEventListener('mousedown', function(e) {
    div.isDown = true;
    div.offset = [
        div.offsetLeft - e.clientX,
        div.offsetTop - e.clientY
    ];
}, true);

div.addEventListener('mouseup', function() {
    div.isDown = false;
}, true);

div.addEventListener('mousemove', function(event) {
    event.preventDefault();
    if (div.isDown) {
        div.mousePosition = {

            x : event.clientX,
            y : event.clientY

        };
        div.style.left = (div.mousePosition.x + div.offset[0]) + 'px';
        div.style.top  = (div.mousePosition.y + div.offset[1]) + 'px';
    }
}, true);
}
person minivip    schedule 20.01.2017
comment
Спасибо за идею с библиотекой. Но div заикается, когда вы быстро двигаете мышью. Событие mousemove должно быть добавлено в документ. - person J. Doe; 12.11.2019

jquery гораздо проще развернуть. Я удивлен, что ты говоришь, что не хочешь этому учиться.

Вы можете сохранить файл jquery на локальном компьютере, чтобы вам не требовался интернет для использования функций jquery.

В моем случае я сохранил его в папке инструментов. Так что мне не нужно быть в Интернете.

Для всех js-многострок js-кода, на которые вы ответили выше, вам нужна только одна маленькая строчка.

 <script src="/common/tools/jquery-1.10.2.js"></script>
 <script src="/common/tools/jquery-ui.js"></script>

 <script>
   $(function() {
   $( "#mydiv_to_make_draggable" ).draggable();
   });
</script>
person webzy    schedule 29.11.2014
comment
JQuery прост в использовании, однако им может пользоваться каждый. Я также программист, который любит кодировать все с нуля. Если вы включаете библиотеку JQuery в свой код и используете только одну или две функции из нее, это занимает много места. - person Vince; 17.08.2015
comment
+1 - сейчас слишком много людей переходят на jQuery, и они (1) не понимают и не осознают, почему jQuery вообще существовал, и (2) оценивают текущую среду на предмет необходимости такой большой библиотеки кода. В большинстве случаев использование чистого ES6 с транспиляцией ES5 приведет к получению кода, проверенного на будущее, меньшему раздуванию и лучшему пониманию программирования и того, как оно работает. Не просто заставить его работать. - person Markus; 06.08.2016

Вы можете использовать это как библиотеку. Работает отлично. Я нашел его на github, но иногда он зависал, потому что пользователь навел указатель мыши на элемент. Я изменил его на документ, и это решило проблему. Это исправленная версия

'use strict';

/**
 * Makes an element draggable.
 *
 * @param {HTMLElement} element - The element.
 */
function draggable(element) {
    var isMouseDown = false;

    // initial mouse X and Y for `mousedown`
    var mouseX;
    var mouseY;

    // element X and Y before and after move
    var elementX = 0;
    var elementY = 0;

    // mouse button down over the element
    element.addEventListener('mousedown', onMouseDown);

    /**
     * Listens to `mousedown` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseDown(event) {
        mouseX = event.clientX;
        mouseY = event.clientY;
        isMouseDown = true;
    }

    // mouse button released
    document.addEventListener('mouseup', onMouseUp);

    /**
     * Listens to `mouseup` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseUp(event) {
        isMouseDown = false;
        elementX = parseInt(element.style.left) || 0;
        elementY = parseInt(element.style.top) || 0;
    }

    // need to attach to the entire document
    // in order to take full width and height
    // this ensures the element keeps up with the mouse
    document.addEventListener('mousemove', onMouseMove);

    /**
     * Listens to `mousemove` event.
     *
     * @param {Object} event - The event.
     */
    function onMouseMove(event) {
        if (!isMouseDown) return;
        var deltaX = event.clientX - mouseX;
        var deltaY = event.clientY - mouseY;
        element.style.left = elementX + deltaX + 'px';
        element.style.top = elementY + deltaY + 'px';
    }
}
person Kyoko Sasagava    schedule 11.08.2020

Accepted answer with touch added

Принятый ответ от adeneo действительно элегантен и хорошо работает. Однако он работает только для щелчков мышью, поэтому вот измененная версия, которая включает сенсорный ввод:

var position;
var offset = [0,0];
var isDown = false;

function makeDraggable(el){

    ['mousedown', 'touchstart'].forEach( evt => 
        el.addEventListener(evt, pickup, true)
    );
    
    ['mousemove', 'touchmove'].forEach( evt => 
        el.addEventListener(evt, move, true)
    );

    ['mouseup', 'touchend'].forEach( evt => 
        el.addEventListener(evt, drop, true)
    );      
        
    function pickup(e) {
        isDown = true;
        if (e.clientX) {
            offset = [el.offsetLeft - e.clientX, el.offsetTop - e.clientY];
        }
        else if (e.touches) {  
            // for touch devices, use 1st touch only
            offset = [el.offsetLeft - e.touches[0].pageX, el.offsetTop - e.touches[0].pageY];
        }       
    }
    function move(e) {
        if (isDown) {
            if (e.clientX) {
                position = {x : e.clientX, y : e.clientY};
            }
            else if (e.touches) {
                position = {x : e.touches[0].pageX, y : e.touches[0].pageY};            
            }           
            el.style.left = (position.x + offset[0]) + 'px';
            el.style.top  = (position.y + offset[1]) + 'px';
        }
    }
    function drop(e) {
        // seems not to be needed for Android Chrome
        // and modern browsers on Mac & PC
        // but is required for iPad & iPhone
        isDown = false;     
        el.style.left = (position.x + offset[0]) + 'px';
        el.style.top  = (position.y + offset[1]) + 'px';
    }
}
person Bob Osola    schedule 26.07.2021