首页 > 解决方案 > 将可拖动元素设置为绝对位置

问题描述

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

我正在使用https://www.w3schools.com/howto/howto_js_draggable.aspdragElement(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);
}

标签: javascripthtmlcsspositiondraggable

解决方案


元素出现在彼此之上,因为您#mydivposition: absolute声明,这将其从文档流中取出。按照 CSS 的规定,不流动的元素将被放置在流动的元素之上。

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

由于translate()将在重新流动(重新验证文档的流动)后生效,即使移动可拖动元素也不会弄乱您的布局。

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

W3Schools (是?)臭名昭著,因为有些过时,有时甚至提出了不好的建议,所以我建议在阅读他们的文章的基础上做一些研究。

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

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

一些题外话:

  • 我强烈建议在使用 CSS 库或类似库之前先学习编写自己的 CSS。
  • 我还强烈建议学习(或至少阅读一次)所有 HTML 标记。这不仅可以帮助您理解语言,还可以让您创建可访问的页面,这对于 SEO 来说是必不可少的。
  • 我还想提一下 Google、Microsoft 和其他公司为制定各种标记标准所做的努力。这也可能与 SEO 相关,但它是一大块阅读材料。

重构

首先,我将删除任何预先存在的评论,以便新评论可以解释更改。

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

var我用constor替换了所有实例let,这取决于看起来更合适的情况。

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

最重要的变化:

  • 在 HTML 中:
    • 可拖动元素现在使用draggable-class
    • 使用正确的标签:
      • 对于标题,使用<header>
      • 如果可拖动元素包含<header>,则将其设为<section>
  • 在 CSS 中:
    • draggable为-class添加规则
    • 为何时添加启用 CSS 规则draggable也是一个<section>
  • 在 JS 中:

// 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;
}
<div id="markers" class="row">
  <!--Change draggable sections to <section> where appropriate-->
  <section id="mydiv" class="col-sm-10 draggable"> <!--Add class "draggable" for styling-->
    <header>Click here to move</header> <!--May have a header; dragElement() now searches for such-->
    <p>Move</p>
    <p>this</p>
    <p>DIV</p>
  </section>
  
  <section id="mydiv2" class="col-sm-10 draggable">
    <header>Click here to move</header>
    <p>Move</p>
    <p>this</p>
    <p>DIV</p>
  </section>
</div>


推荐阅读