将可拖动元素设置为绝对位置

时间:2021-05-24 10:26:23

标签: javascript html css position draggable

我有一些元素想要拖到画布上以在之后返回它们的坐标。 所以元素应该彼此相邻出现,而不是我将能够拖动它们。 我面临的问题是元素彼此之间没有空格分隔:

我正在使用 https://www.w3schools.com/howto/howto_js_draggable.asp 中的函数 dragElement(element) 使我的元素可拖动。

//Make the DIV element draggagle:
dragElement(document.getElementById("mydiv"));
dragElement(document.getElementById("mydiv2"));

function dragElement(elmnt) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) {
    // if present, the header is where you move the DIV from:
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    // otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  position: absolute;
  z-index: 9;
  background-color: #f1f1f1;
  text-align: center;
  border: 1px solid #d3d3d3;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  cursor: move;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}
<div class="container">
  <div id="markers" class="row">
    <div id="mydiv" class="col-sm-10">
      <div id="mydivheader">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
    <div id="mydiv2" class="col-sm-10">
      <div id="mydivheader2">Click here to move</div>
      <p>Move</p>
      <p>this</p>
      <p>DIV</p>
    </div>
  </div>
</div>

我尝试将 CSS 位置属性从绝对更改为相对,它使元素设置为彼此相邻,但我无法再拖动它们。

在我发布的代码中,我只有两个元素,但实际上,将使用 JS 代码动态创建更多新元素。

这是我的 JS 代码:

//this list will be uploaded by the user      
var d = ["module1", "module2",.......,"module22"];

for(i in d){
  append_module_name_to_drag_div(d[i]);
  dragElement(document.getElementById(d[i]));  
}

function append_module_name_to_drag_div(module_name){
  var mydiv = document.createElement("div");
  mydiv.id=module_name;
  mydiv.className ='col-sm-10';

  var mydivmarker= document.createElement("div");
  mydivmarker.id=module_name+"header";
  mydivmarker.className ='mydivheader';
  
  var marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  
  mydivmarker.append(marker_image);
  mydiv.innerHTML=module_name;
  mydiv.append(mydivmarker);
  document.getElementById("markers").appendChild(mydiv);
}

3 个答案:

答案 0 :(得分:0)

原始 w3schools 示例

您可以像链接的 w3schools 示例中提到的那样使用 position: absolute,但无需为绝对定位的元素定义 topleft 等,它们都具有相同的默认值:{{1 }} 和 top: 0。因此它们都出现在同一个位置,从而导致彼此堆叠。

如果为每个绝对定位的元素为 left: 0top 等定义不同的值,则可以防止堆叠。例如:

left

这是针对您最初发布的问题的解决方案,其中包含两个硬编码的可拖动元素,但它不太适合动态添加的内容。

工作示例:

为简单起见,我省略了函数 #mydiv2 { top: 170px; } 中的第一个 if ... else 块,并将事件侦听器直接附加到整个元素:

dragElement()

elmnt.onmousedown = dragMouseDown;
//Make the DIV element draggagle:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv, 
#mydiv2 {
  position :absolute;
  z-index: 9;
  background-color: #f1f1f1;
  text-align: center;
  cursor: move;
  border: 1px solid #d3d3d3;
}

#mydiv2 {
  top: 170px;
}

#mydivheader, 
#mydivheader2 {
  padding: 10px;
  z-index: 10;
  background-color: #2196F3;
  color: #fff;
}


使用<div class="container"> <div class="row"> <div id="mydiv" class="col-sm-10"> <div id="mydivheader">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> <div id="mydiv2" class="col-sm-10"> <div id="mydivheader2">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> </div> </div>

基本实现

对于动态添加的内容(或任何其他不使用 transform: translate()top 等的原因),最好使用 left 并省略 transform: translate(),以便元素保持原样,无法堆叠。

要使其发挥作用,您只需从旧的 position: absolute 值中减去原始计算值,然后用该减法的值覆盖该值(以保存它们以供下一次函数调用):

translate

最后,您必须将发生样式的两行从 pos1 = pos1 - (pos3 - e.clientX); pos2 = pos2 - (pos4 - e.clientY); top 更改为带有 left 的一行:

translate

工作示例:

我将过时的 elmnt.style.transform = 'translate(' + (pos1) + "px, " + (pos2) + "px)"; 换成了 var,因为变量会改变。

let
//Make the DIV element draggagle:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos1 - (pos3 - e.clientX);
    pos2 = pos2 - (pos4 - e.clientY);
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.transform = 'translate(' + (pos1) + "px, " + (pos2) + "px)";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  width: 160px;
  background-color: #f1f1f1;
  text-align: center;
  cursor: move;
  border: 1px solid #d3d3d3;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}

口述变量

w3schools 示例 <div class="container"> <div class="row"> <div id="mydiv" class="col-sm-10"> <div id="mydivheader">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> <div id="mydiv2" class="col-sm-10"> <div id="mydivheader2">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> </div> </div> 中变量的名称不是很直观。因此,应将它们重命名以易于理解它们的用途:pos1, pos2, pos3, pos4translate = { x: 0, y: 0 }

工作示例:

client = { x: 0, y: 0 }
//Make the DIV element draggagle:
dragElement(document.getElementById("mydiv2"));
dragElement(document.getElementById("mydiv"));

function dragElement(elmnt) {
  let translate = { x: 0, y: 0 };
  let client = { x: 0, y: 0 };
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    client.x = e.clientX;
    client.y = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    translate.x = translate.x - (client.x - e.clientX);
    translate.y = translate.y - (client.y - e.clientY);
    client.x = e.clientX;
    client.y = e.clientY;
    // set the element's new position:
    elmnt.style.transform = 'translate(' + (translate.x) + "px, " + (translate.y) + "px)";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
#mydiv,
#mydiv2 {
  width: 160px;
  border: 1px solid #d3d3d3;
  text-align: center;
  cursor: move;
  background-color: #f1f1f1;
}

#mydivheader,
#mydivheader2 {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}

最终实施

为了演示您稍后添加的用于动态添加可拖动元素的函数的代码,这里是最终实现。我将过时的 <div class="container"> <div class="row"> <div id="mydiv" class="col-sm-10"> <div id="mydivheader">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> <div id="mydiv2" class="col-sm-10"> <div id="mydivheader2">Click here to move</div> <p>Move</p> <p>this</p> <p>DIV</p> </div> </div> </div>var 交换,因为变量不会改变。

工作示例:

const
const d = ["module1", "module2", "module3", "module4", "module5"];

for(i in d) {
  append_module_name_to_drag_div(d[i]);
  dragElement(document.getElementById(d[i]));  
}
  
function append_module_name_to_drag_div(module_name) {
  const mydiv = document.createElement("div");
  mydiv.id = module_name;
  mydiv.className = 'module col-sm-10';

  const mydivmarker = document.createElement("div");
  mydivmarker.id = module_name+"header";
  mydivmarker.className = 'mydivheader';
  
  const marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  
  mydivmarker.append(marker_image);
  mydiv.innerHTML = module_name;
  mydiv.append(mydivmarker);
  document.getElementById("markers").appendChild(mydiv);
}

function dragElement(elmnt) {
  let translate = { x: 0, y: 0 };
  let client = { x: 0, y: 0 };
  
  elmnt.onmousedown = dragMouseDown;

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    client.x = e.clientX;
    client.y = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    translate.x = translate.x - (client.x - e.clientX);
    translate.y = translate.y - (client.y - e.clientY);
    client.x = e.clientX;
    client.y = e.clientY;
    // set the element's new position:
    elmnt.style.transform = 'translate(' + (translate.x) + "px, " + (translate.y) + "px)";
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
}
.module {
  width: 80px;
  border: 1px solid #d3d3d3;
  text-align: center;
  cursor: move;
  background-color: #f1f1f1;
}

.mydivheader {
  padding: 10px;
  background-color: #2196F3;
  color: #fff;
}

答案 1 :(得分:0)

元素出现在彼此的顶部是因为您的 #mydiv 声明了 position: absolute,这将其从文档流中移除。流出的元素将被放置在流动元素的顶部,由 CSS 指定。

Greg 建议使用 CSS 函数 translate() 进行移动,而不是使用 position: absolutetop 等。这会使元素留在流中,这意味着它们将还在考虑中。

由于 translate() 会在重排(重新验证文档流)后生效,因此即使可拖动元素被移动,它也不会干扰您的布局。

如果您只对结果感兴趣,请跳过。但这里有更多信息。

W3Schools 因有些过时或有时甚至提供糟糕的建议而臭名昭著,所以我建议在阅读他们的文章的基础上做一些研究。< /p>

现在,W3Schools' dragElement() 函数非常适合向后兼容,因为它是为 2011 年及更早版本的浏览器编写的。如今,大多数(如果不是全部)浏览器都实现了 HTML5、CSS3 和(至少)ES6(JavaScript 规范),以及某些浏览器标准。

这意味着将支持以下(以及更多!):

  • const/let 的使用。
    它们与 var 之间的区别:它们声明块范围变量,并且不会被提升。但是,使用它们将使调试更容易,并会传达以下信息:
    • const 表示引用永远不会改变。
    • let 表示引用可能被更改。
  • 使用 Element.querySelector() 搜索元素的后代。 (类似于Document.querySelector()。)
  • addEventListener()/removeEventListener() 的使用。
    这解决了只能分配的问题 使用 onevent-listeners 时元素的一个事件侦听器。
  • (lambda) arrow-functions 的使用。经常可以作为 函数的替代方法,但它们未提升
  • 更多语义 HTML 标签!这非常重要,因为它们的使用是 (IMO) web-accessibility 的主要焦点。

一些题外话:

  • 我强烈建议在使用 CSS 库或类似库之前先学习编写自己的 CSS。
  • 我还强烈建议您学习(或至少阅读一次)所有 HTML 标签。这不仅有助于您理解语言,还可以让您创建可访问的页面,这对于 SEO 来说是必不可少的。
  • 我还想提及 Google、Microsoft 和其他公司在 standard of marking up 各种各样的东西上所做的努力。这可能也与 SEO 相关,但需要大量阅读材料。

重构

首先,我将删除所有先前存在的评论,以便新评论可以解释更改。

对于字符串,有些人更喜欢 '(单引号)而不是 "(双引号),因为您更可能希望使用 " 而不是 {{1} } 在一个字符串中。使用一个允许不转义使用另一个。
我更喜欢 JS 中的 ',所以不要对我的更改使用它们的原因感到困惑。

我将 ' 的所有实例替换为 varconst,具体取决于哪个看起来更合适。

代码现在将使用 let/addEventListener() 而不是 removeEventListener()-listener。

最重要的变化:

  • 在 HTML 中:
    • 可拖动元素现在使用 onevent-class
    • 使用正确的标签:
      • 对于标题,使用 draggable
      • 如果可拖动元素包含 <header>,请将其设为 <header>
  • 在 CSS 中:
  • 在 JS 中:

translate()
// Make all `.draggable` draggable
for (const el of document.querySelectorAll('.draggable'))
  dragElement(el);

// May be changed? Hence `let`
let d = ["module1", "module2", "module22"];

// Changed loop to for...of, because we only want to iterate over the array-elements
for (const el of d) {
  append_module_name_to_drag_div(el);
  dragElement(document.getElementById(el));  
}

function append_module_name_to_drag_div(module_name){
  const mydiv = document.createElement("div");

  mydiv.id = module_name;
  mydiv.className = 'col-sm-10 draggable'; // Added '.draggable'

  const mydivmarker = document.createElement("div");
  const marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  mydivmarker.append(marker_image);
  
  // Generally bad to use .innerHTML with user-generated content.
  // Prefer .innerText or .textContent.
  // Read more on:
  // https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations
  mydiv.innerText = module_name;
  
  mydiv.append(mydivmarker);
  
  // Changed to .append(), try to stay consistent!
  // I suggest using .append() over .appendChild(), as the former allows
  // an arbitrary amount of arguments.
  document.getElementById("markers").append(mydiv);
}

// Originally from: "How to create a draggable HTML element" - W3Schools
// ( https://www.w3schools.com/howto/howto_js_draggable.asp )
function dragElement(el) {
  // We'll only need x- and y-positions; may/will change, hence `let`
  let xPos = 0;
  let yPos = 0;
  if (el.querySelector('header')) // Use Element.querySelector() to avoid using unnecessary IDs
    el.querySelector('header').addEventListener('mousedown', dragMouseDown);
  else
    el.addEventListener('mousedown', dragMouseDown);
  
  function dragMouseDown(event) {
    // The event-object will always be passed; no need to refer to window.event
    event.preventDefault();
    
    // No need to save initial mouse-position
    
    document.addEventListener('mouseup', closeDragElement);
    document.addEventListener('mousemove', elementDrag);
  }
  function elementDrag(event) {
    event.preventDefault();
    
    // New positions calculated using MouseEvent.(movementX|movementY)
    xPos += event.movementX;
    yPos += event.movementY;
    
    // Set position as translate() of .style.transform
    el.style.transform = `translate(${xPos}px, ${yPos}px)`;
  }
  function closeDragElement() {
    document.removeEventListener('mouseup', closeDragElement);
    document.removeEventListener('mousemove', elementDrag);
  }
}
/* Add enabling CSS for .draggable */
/* Enabling CSS:
 * Only declare properties when rules apply.
 */
/* Disabling CSS:
 * Declare all "default" properties.
 * Reset all then-unwanted properties.
 * May overwrite other rule's declarations in the process.
 */
/* Related: https://www.silvestar.codes/articles/you-want-a-single-enabling-selector-not-the-one-that-disables-the-rule-of-the-previous-one/ */

.draggable {width: fit-content} /* Default */
.draggable:not(section) {cursor: move} /* Enabling CSS */
section.draggable {
  border: 1px solid #d3d3d3;
  text-align: center;
  background-color: #f1f1f1;
}
section.draggable>header {
  padding: .625rem;
  color: #fff;
  background-color: #2196F3;
  cursor: move;
}

答案 2 :(得分:0)

我发现的最好方法是每次都为每个元素设置 topleft,这将使每个元素处于单独的位置而不会破坏可抓取的部分:

const d = ["module1", "module2", "module3", "module4", "module5"];



 for(i in d){
      append_module_name_to_drag_div(d[i],top,left);
      dragElement(document.getElementById(d[i]));  

      if(left<220){left=left+39;}
      //you can set here height and width: in my case 300 was the width of the container, 35 was the width of markers
      else{
        left=5;
        top=top+70;//70 height of marker
      }
    }

我修改了这个函数,增加了两个参数来指定顶部和左侧:

function append_module_name_to_drag_div(module_name,top,left){
  var mydiv = document.createElement("div");
  
  mydiv.id=module_name;
  mydiv.className ='col-sm-10';// 'col-md-3 col-lg-3 col-xl-3 col-sm-6';//'mydiv';

  var mydivmarker= document.createElement("div");
  mydivmarker.id=module_name+"header";
  mydivmarker.className ='mydivheader';// 'col-sm-4';//'mydivheader';
  var marker_image = new Image();
  marker_image.src = "http://www.clker.com/cliparts/w/O/e/P/x/i/map-marker-hi.png";
  marker_image.height = 45;
  marker_image.width = 35;
  mydivmarker.append(marker_image);

  // var p = document.createElement("p");
  mydiv.innerHTML=module_name;
  // mydiv.append(p);
  mydiv.append(mydivmarker);

  ///***
  // mydiv.style.cssFloat = "left";
  mydiv.style.left = left.toString()+"px";
  mydiv.style.top = top.toString()+"px";
///**** */

  document.getElementById("markers").appendChild(mydiv);
  var linebreak = document.createElement("br");
  document.getElementById("markers").appendChild(linebreak);
  
  
}

W3Schools 的函数除了两行 where(//set the element's new position:)

function dragElement(elmnt) {
  
  var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) {
    /* if present, the header is where you move the DIV from:*/
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    /* otherwise, move the DIV from anywhere inside the DIV:*/
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  }

  function closeDragElement() {
    /* stop moving when mouse button is released:*/
    document.onmouseup = null;
    document.onmousemove = null;
  }
}