Подвижный / перетаскиваемый ‹div›

Это мой обновленный и измененный сценарий, он работает полностью, за исключением того, что я хотел бы сделать его универсальным ... обратите внимание на ****, как я могу сделать это так, чтобы мне не приходилось делать function(e){BOX.Draggable.elemen = e.target || e.srcElement; elementDraggable(e); каждый раз, когда мне нужно использовать перетаскиваемый функция для другого элемента?

window.onload = addListeners;

var BOX = function(){
  return{
    Draggable: function(){}
  };
}();

function addListeners(){
  document.getElementById('div').addEventListener('contextmenu', menumove, false);
  **document.getElementById('div').addEventListener('mousedown', function(e){BOX.Draggable.elemen = e.target || e.srcElement; elementDraggable(e);}, false);**
}

function elementDraggable(e){
  var e = e || window.event;
  var div = BOX.Draggable.elemen;
  BOX.Draggable.innerX = e.clientX + window.pageXOffset - div.offsetLeft;
  BOX.Draggable.innerY = e.clientY + window.pageYOffset - div.offsetTop;

  window.addEventListener('mousemove', elementMove, false);
  window.addEventListener('mouseup', function(){
    window.removeEventListener('mousemove', elementMove, false);
    }, true);

  function elementMove(e){
    div.style.position = 'absolute';
    div.style.left = e.clientX + window.pageXOffset - BOX.Draggable.innerX + 'px';
    div.style.top = e.clientY + window.pageYOffset - BOX.Draggable.innerY + 'px';
  }
}

person person0    schedule 17.02.2012    source источник
comment
У этого сообщения гораздо более простая версия JS stackoverflow.com/questions/24050738/   -  person MrWiLofDoom    schedule 24.05.2018


Ответы (7)


Подходит ли вам jQuery? Это делает то, что вы делаете, очень простым, поскольку код уже существует.

http://jqueryui.com/demos/draggable/

Демо

Код JavaScript

window.onload = addListeners;

function addListeners(){
    document.getElementById('dxy').addEventListener('mousedown', mouseDown, false);
    window.addEventListener('mouseup', mouseUp, false);

}

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

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

function divMove(e){
    var div = document.getElementById('dxy');
  div.style.position = 'absolute';
  div.style.top = e.clientY + 'px';
  div.style.left = e.clientX + 'px';
}​
person jnoreiga    schedule 17.02.2012
comment
Я уже знаю, как это сделать с помощью jquery, я пытаюсь научиться делать это вручную с помощью javascript, спасибо. - person person0; 17.02.2012
comment
да, он не сохранил последнее, я просто понял это. Я это переписываю - person jnoreiga; 18.02.2012
comment
Ok. теперь все должно быть в порядке. Не могу поверить, что он не обновил его с первого раза и потерял. - person jnoreiga; 18.02.2012
comment
Спасибо, я уже понял большую часть этого, единственной частью, которой у меня не было, была часть removeEventListener на mouseup. - person person0; 18.02.2012
comment
Я заметил, что при перетаскивании мышь перескакивает в верхний левый угол div - как это исправить? - person Anderson Green; 05.12.2012
comment
@AndersonGreen Вы должны принять во внимание позицию начала перетаскивания. Если вы начинаете перетаскивать с середины div, вам нужно сместить позицию на это. См. jsfiddle.net/wfbY8/737 (старый комментарий, который я знаю, но требует ответа ..) - person FrancescoMM; 11.03.2013
comment
jquery ui очень тяжелый! вам не следует устанавливать полную библиотеку из-за одной простой вещи. Я знаю, что jquery ui прост в использовании, но мы, разработчики, должны начать думать с точки зрения малого ... особенно для мобильных устройств. - person Oneezy; 01.04.2014
comment
Это правда MrRioku, но если бы он уже загружал библиотеку, это было бы его лучшим вариантом. Это было не так, поэтому я предоставил ему прямой код JavaScript. - person jnoreiga; 01.04.2014
comment
Есть ли причина, по которой функции mouseDown и mouseUp имеют параметр useCapture, установленный на true при присоединении событий? В то время как функция addListeners использует false при прикреплении событий? - person ayjay; 09.01.2015

Это хороший скрипт без jQuery для перетаскивания div: http://jsfiddle.net/g6m5t8co/1/

<!doctype html>
<html>
    <head>
        <style>
            #container {
                position:absolute;
                background-color: blue;
                }
            #elem{
                position: absolute;
                background-color: green;
                -webkit-user-select: none;
                -moz-user-select: none;
                -o-user-select: none;
                -ms-user-select: none;
                -khtml-user-select: none;     
                user-select: none;
            }
        </style>
        <script>
            var mydragg = function(){
                return {
                    move : function(divid,xpos,ypos){
                        divid.style.left = xpos + 'px';
                        divid.style.top = ypos + 'px';
                    },
                    startMoving : function(divid,container,evt){
                        evt = evt || window.event;
                        var posX = evt.clientX,
                            posY = evt.clientY,
                        divTop = divid.style.top,
                        divLeft = divid.style.left,
                        eWi = parseInt(divid.style.width),
                        eHe = parseInt(divid.style.height),
                        cWi = parseInt(document.getElementById(container).style.width),
                        cHe = parseInt(document.getElementById(container).style.height);
                        document.getElementById(container).style.cursor='move';
                        divTop = divTop.replace('px','');
                        divLeft = divLeft.replace('px','');
                        var diffX = posX - divLeft,
                            diffY = posY - divTop;
                        document.onmousemove = function(evt){
                            evt = evt || window.event;
                            var posX = evt.clientX,
                                posY = evt.clientY,
                                aX = posX - diffX,
                                aY = posY - diffY;
                                if (aX < 0) aX = 0;
                                if (aY < 0) aY = 0;
                                if (aX + eWi > cWi) aX = cWi - eWi;
                                if (aY + eHe > cHe) aY = cHe -eHe;
                            mydragg.move(divid,aX,aY);
                        }
                    },
                    stopMoving : function(container){
                        var a = document.createElement('script');
                        document.getElementById(container).style.cursor='default';
                        document.onmousemove = function(){}
                    },
                }
            }();

        </script>
    </head>
    <body>
        <div id='container' style="width: 600px;height: 400px;top:50px;left:50px;">     
            <div id="elem" onmousedown='mydragg.startMoving(this,"container",event);' onmouseup='mydragg.stopMoving("container");' style="width: 200px;height: 100px;">
                <div style='width:100%;height:100%;padding:10px'>
                <select id=test>
                    <option value=1>first
                    <option value=2>second
                </select>
                <INPUT TYPE=text value="123">
                </div>
            </div>
        </div>  
    </body>
</html>
person niente00    schedule 19.09.2014
comment
Отличное решение! Он довольно общий и гарантирует, что вы не сможете переместить div за пределы родительского контейнера! - person Philipp; 01.09.2016
comment
Да отличный пример! Другая проблема заключается в том, что если вы начинаете перетаскивать курсор за пределы зеленого поля, мышь вверх не регистрируется .... другими словами, событие подъема мыши не регистрируется за пределами зеленого поля. - person Dawit; 28.10.2016
comment
Даввит, добавьте onmouseout для контейнера. - person Harijs Krūtainis; 12.12.2016
comment
Какая польза от строки var a = document.createElement('script'); в функции stopMoving? - person Iwazaru; 11.01.2017
comment
это намного проще: stackoverflow.com/questions/24050738/ - person MrWiLofDoom; 24.05.2018
comment
Я очень ценю ваш код! В настоящее время я использую stackoverflow.com/questions/24050738/, что, на мой взгляд, является более простым способом начать и изучить это. Затем, вдохновленный вашим, я хочу заблокировать перетаскивание за границы. - person Ryan; 14.04.2021

Что ж, ваш код движения упрощается до:

div.style.position = "absolute";
div.style.top = e.clientY - (e.clientY - div.offsetTop) + "px";
div.style.left = e.clientX - (e.clientX - div.offsetLeft) + "px";

Здесь базовая математика - e.clientX и e.clientY абсолютно не влияют на позицию здесь, поэтому вы просто берете offsetLeft и переназначаете его на style.left, и то же самое для вершины. Таким образом, никакого движения.

Что вам нужно сделать, так это сохранить clientX и clientY, когда произойдет mousedown, и выполнить вычитание на основе этого.

О, и вы также неправильно устанавливаете прослушиватель событий. В нынешнем виде он запускается divMove один раз, а возвращаемое значение (здесь undefined) - это функция, прикрепленная как слушатель. Вместо этого используйте function(e) {divMove(dxy,e || window.event);}.

person Niet the Dark Absol    schedule 17.02.2012
comment
Я в основном понимаю, что вы говорите, и вижу, что вы правы. Но я не совсем уверен, как изменить это на то, что вы говорите. не могли бы вы разместить отрывок, чтобы показать мне, что именно вы имеете в виду? - person person0; 17.02.2012
comment
В комментарии к другому ответу вы сказали, что пытаетесь изучить, поэтому я не буду делать это за вас, но в основном у вас есть два основных способа сделать это. 1) Используйте абсолютное позиционирование, получите текущие координаты мыши и установите элемент в эти координаты. 2) Используйте относительное позиционирование и возьмите разницу между текущей позицией и начальной позицией. - person Niet the Dark Absol; 17.02.2012
comment
Спасибо, что заставили меня сделать это самостоятельно. Я смог разобраться почти во всем, и мне просто нужна была небольшая помощь, потому что я не знал о методе removeEventListener :-) - person person0; 18.02.2012
comment
Ура ^ _ ^ В моем собственном коде перетаскивания просто используется elem.onmousemove, устанавливая его на null вместо removeEventListener. Личное предпочтение. - person Niet the Dark Absol; 18.02.2012

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

function makeDragable(dragHandle, dragTarget) {
  let dragObj = null; //object to be moved
  let xOffset = 0; //used to prevent dragged object jumping to mouse location
  let yOffset = 0;

  document.querySelector(dragHandle).addEventListener("mousedown", startDrag, true);
  document.querySelector(dragHandle).addEventListener("touchstart", startDrag, true);

  /*sets offset parameters and starts listening for mouse-move*/
  function startDrag(e) {
    e.preventDefault();
    e.stopPropagation();
    dragObj = document.querySelector(dragTarget);
    dragObj.style.position = "absolute";
    let rect = dragObj.getBoundingClientRect();

    if (e.type=="mousedown") {
      xOffset = e.clientX - rect.left; //clientX and getBoundingClientRect() both use viewable area adjusted when scrolling aka 'viewport'
      yOffset = e.clientY - rect.top;
      window.addEventListener('mousemove', dragObject, true);
    } else if(e.type=="touchstart") {
      xOffset = e.targetTouches[0].clientX - rect.left;
      yOffset = e.targetTouches[0].clientY - rect.top;
      window.addEventListener('touchmove', dragObject, true);
    }
  }

  /*Drag object*/
  function dragObject(e) {
    e.preventDefault();
    e.stopPropagation();

    if(dragObj == null) {
      return; // if there is no object being dragged then do nothing
    } else if(e.type=="mousemove") {
      dragObj.style.left = e.clientX-xOffset +"px"; // adjust location of dragged object so doesn't jump to mouse position
      dragObj.style.top = e.clientY-yOffset +"px";
    } else if(e.type=="touchmove") {
      dragObj.style.left = e.targetTouches[0].clientX-xOffset +"px"; // adjust location of dragged object so doesn't jump to mouse position
      dragObj.style.top = e.targetTouches[0].clientY-yOffset +"px";
    }
  }

  /*End dragging*/
  document.onmouseup = function(e) {
    if (dragObj) {
      dragObj = null;
      window.removeEventListener('mousemove', dragObject, true);
      window.removeEventListener('touchmove', dragObject, true);
    }
  }
}

makeDragable('#handle', '#moveable')
#moveable {
    width: 100px;
    height: 100px;
    background: blue;
}

#handle {
    width: 50px;
    height: 50px;
    cursor: move;
    background: red;
}
<div id="moveable">
    <div id="handle">
    </div>
</div>

person ospider    schedule 01.12.2017
comment
Это определенно должен быть принятый ответ: общий подход, простой в использовании и даже поддерживает сенсорные события. Да, и здесь учитывается, что часто перетаскиваемый контейнер будет отличаться от ручки, которой вы его перетаскиваете. Большое спасибо!! :) - person mh166; 25.06.2021

Попробовав принятый ответ jnoreiga, меня очень раздражало то, что перетаскиваемый элемент внезапно привязывается к верхнему левому углу, вместо того, чтобы сохранять то же относительное положение.

Этот фрагмент предотвращает неудобное вышеупомянутое поведение с помощью смещения и предоставляет простой интерфейс для создания перетаскиваемых элементов по одному или в массовом порядке с помощью вызова forEach или аналогичного.

window.onload = function() {
  draggable(document.getElementById('foo'));
}

function draggable(el) {
  el.addEventListener('mousedown', function(e) {
    var offsetX = e.clientX - parseInt(window.getComputedStyle(this).left);
    var offsetY = e.clientY - parseInt(window.getComputedStyle(this).top);
    
    function mouseMoveHandler(e) {
      el.style.top = (e.clientY - offsetY) + 'px';
      el.style.left = (e.clientX - offsetX) + 'px';
    }

    function reset() {
      window.removeEventListener('mousemove', mouseMoveHandler);
      window.removeEventListener('mouseup', reset);
    }

    window.addEventListener('mousemove', mouseMoveHandler);
    window.addEventListener('mouseup', reset);
  });
}
/* The only required styling is position: absolute */
#foo {
  position: absolute;
  border: 1px solid black;
  overflow: hidden;
}

/* Prevents inconsistent highlighting of element while being dragged
   Copied from https://stackoverflow.com/questions/826782 */
.noselect {
  -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome and Opera */
}
<div id="foo" class="noselect">This is a draggable div!</div>

person Evan    schedule 21.05.2019

Дополнительный метод к коду "niente00".

init : function(className){
    var elements = document.getElementsByClassName(className);
    for (var i = 0; i < elements.length; i++){
        elements[i].onmousedown = function(){mydragg.startMoving(this,'container',event);};
        elements[i].onmouseup = function(){mydragg.stopMoving('container');};
        }
    }
person Harijs Krūtainis    schedule 12.12.2016

Любое решение, использующее clientY, clientX, pageY или pageX в событии dragend , полностью выйдет из строя в Firefox. Источник: Bugzilla: Ошибка № 505521, установка координат экрана во время события перетаскивания HTML5.

Как нам обойти это ограничение? Событие drop document также запускается в то же самое время, что и событие dragend перетаскиваемого элемента. Но мы можем видеть такие вещи, как clientY и clientX здесь, в firefox. Итак, давайте просто воспользуемся этим.

Две рабочие демонстрации, решение, полностью состоящее только из JavaScript: фрагмент кода SO и JSBin.

var startx = 0;
var starty = 0;
dragStartHandler = function(e) {
  startx = e.clientX;
  starty = e.clientY;
}

dragOverHandler = function(e) {
  e.preventDefault();
  return false;
}

dragEndHandler = function(e) {
  if(!startx || !starty) {
    return false;
  }
  
  var diffx = e.clientX - startx;
  var diffy = e.clientY - starty;
  
  var rect = e.target.getBoundingClientRect();

var offset = { 
                top: rect.top + window.scrollY, 
                left: rect.left + window.scrollX, 
            };
  
  var newleft = offset.left + diffx;
  var newtop = offset.top + diffy;
  
  e.target.style.position = 'absolute';
  e.target.style.left = newleft + 'px';
  e.target.style.top = newtop + 'px';
  
  startx = 0;
  starty = 0;
}

document.getElementsByClassName("draggable")[0].addEventListener('dragstart', dragStartHandler);

document.addEventListener('dragover', dragOverHandler);
document.addEventListener('drop', dragEndHandler);
.draggable {
  border: 1px solid black;
  cursor: move;
  width:250px;
};
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  
  <BR><BR><BR>

  <div id="draggable1" class="draggable" draggable="true">
    Hey, try to drag this element!
  </div>
  
</body>
</html>

Объяснение:

  • dragStartHandler(): это привязано к перетаскиваемому элементу. Здесь все, что мы делаем, это записываем текущие координаты x / y при запуске.
  • dragOverHandler(): Это привязано к документу, поэтому мы можем переопределить поведение перетаскивания по умолчанию. Это необходимо для перетаскивания любого типа.
  • dragEndHandler(): Это связано с document drop. Обычно мы хотим, чтобы это было связано с element dragend, но, поскольку clientY и clientX отсутствуют, мы привязываем его к документу. Это именно то, что вы хотели бы получить при вызове dragend, за исключением того, что у вас есть координаты x / y.

Используемая формула:

set style to: (current position) - (original position)

Это настолько сложно, насколько это возможно, но для расчета и применения стиля только для измерения x код ...

var diffx = e.clientX - startx;
var rect = e.target.getBoundingClientRect();
var offset = { 
        left: rect.left + window.scrollX, 
    };
var newleft = offset.left + diffx;
e.target.style.position = 'absolute';
e.target.style.left = newleft + 'px';
person HoldOffHunger    schedule 11.08.2020