[英]Make a draggable element whitout setting position to absolute
I have some element that I want to drag on a canvas to return their coordinates thereafter.我有一些元素要拖到 canvas 上,然后返回它们的坐标。 So elements should appear next to each other than I will be able to drag them.
所以元素应该彼此相邻出现,而不是我能够拖动它们。 The problem I am facing is the elements appear on each other not separated by space:
我面临的问题是元素彼此出现而不用空格分隔:
I am using the function dragElement(element)
from https://www.w3schools.com/howto/howto_js_draggable.asp to make my elements draggable.我正在使用https://www.w3schools.com/howto/howto_js_draggable.asp中的 function
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>
I tried to change the CSS position property from absolute to relative, It make elements set next to each other but I can't drag them anymore.我试图将 CSS position 属性从绝对更改为相对,它使元素彼此相邻,但我不能再拖动它们了。
On the code I posted I have only two elements but actually, more new elements will be created dynamically with a JS code.在我发布的代码中,我只有两个元素,但实际上,将使用 JS 代码动态创建更多新元素。
Here is my JS code:这是我的 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);
}
You can use position: absolute
like mentioned on the linked w3schools example, but without defining top
, left
etc. for the absolutely positioned elements they all have the same default: top: 0
and left: 0
.您可以使用
position: absolute
就像在链接的 w3schools 示例中提到的那样,但是没有为绝对定位的元素定义top
, left
等,它们都具有相同的默认值: top: 0
和left: 0
。 Therefor they all appear at the same location, which leads to the stacking on top of each other.因此,它们都出现在同一位置,这导致彼此堆叠。
You can prevent the stacking if you define a different value for top
, left
etc. for each absolutely positioned element.如果为每个绝对定位的元素为
top
、 left
等定义不同的值,则可以防止堆叠。 For example:例如:
#mydiv2 {
top: 170px;
}
This is a solution for your originally posted question with two hardcoded draggable elements, but it doesn't fit well for dynamically added content.这是您最初发布的问题的解决方案,其中包含两个硬编码的可拖动元素,但它不适合动态添加的内容。
Working example:工作示例:
For simplicity i omitted the first if... else
block in the function dragElement()
and attached the event listener directly to the whole element:为简单起见,我省略了 function
dragElement()
中的第一个if... else
块,并将事件侦听器直接附加到整个元素:
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()
transform: translate()
For dynamically added content (or any other reason for not using top
, left
etc.) it would be better to use transform: translate()
and omit position: absolute
, so that the elements stay where they are and can't get stacked.对于动态添加的内容(或任何其他不使用
top
、 left
等的原因),最好使用transform: translate()
并省略position: absolute
,以便元素保持原位并且不能堆叠。
To bring this to work you simply have to subtract the original calculation from the old translate
-values, which you overwrite with the values of that subtraction (to save them for the next function call):要实现这一点,您只需从旧的
translate
值中减去原始计算,然后用该减法的值覆盖(保存它们以供下一次 function 调用):
pos1 = pos1 - (pos3 - e.clientX);
pos2 = pos2 - (pos4 - e.clientY);
Last you have to change the two lines where the styling occurs from top
and left
to one line with translate
:最后,您必须使用
translate
将样式从top
和left
发生的两行更改为一行:
elmnt.style.transform = 'translate(' + (pos1) + "px, " + (pos2) + "px)";
Working example:工作示例:
I exchanged the outdated var
with let
because the vars will change.我用
let
交换了过时的var
,因为变量会改变。
//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; }
<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>
The names of the vars in the w3schools example pos1, pos2, pos3, pos4
aren't very intuitive. w3schools 示例
pos1, pos2, pos3, pos4
中的变量名称不是很直观。 Therefor they should be renamed to easyly understand what they are for: translate = { x: 0, y: 0 }
and client = { x: 0, y: 0 }
.因此,它们应该重命名以易于理解它们的用途:
translate = { x: 0, y: 0 }
和client = { x: 0, y: 0 }
。
Working example:工作示例:
//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>
To demonstrate the code with your later added function for dynamically adding draggable elements, here the final implementation.为了演示您后来添加的用于动态添加可拖动元素的 function 的代码,这里是最终实现。 I exchanged the outdated
var
with const
because the vars won't change.我用
const
交换了过时的var
,因为 var 不会改变。
Working example:工作示例:
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; }
<div class="container"> <div id="markers" class="row"> </div> </div>
The elements appear on top of each other because your #mydiv
has position: absolute
declared, which takes it out of the document flow.元素出现在彼此之上,因为您的
#mydiv
具有position: absolute
声明,这将其从文档流中取出。 Elements out of flow will be placed on top of the flowing elements, as specified by CSS.按照 CSS 的规定,不流动的元素将放置在流动的元素之上。
Greg suggested to use the CSS-function translate()
for moving, instead of position: absolute
with top
etc. This would leave the elements in the flow, meaning they will still be considered. Greg建议使用 CSS 函数
translate()
来移动,而不是position: absolute
with top
等。这将使元素留在流中,这意味着它们仍将被考虑。
As translate()
will take effect after re-flow (re-validating the flow of the document), it won't mess with your layout even when the draggable elements are moved.由于
translate()
将在重新流动(重新验证文档的流动)后生效,因此即使移动可拖动元素也不会弄乱您的布局。
If you're only interested in the result, skip ahead.如果您只对结果感兴趣,请跳过。 But here's some more information.
但这里有更多信息。
W3Schools was (is?) notorious for being somewhat outdated or sometimes even featuring bad advice, so I advise to do some research on top of reading their articles. W3Schools (是?)臭名昭著,因为有些过时,有时甚至提供了糟糕的建议,所以我建议在阅读他们的文章的基础上做一些研究。
Now, W3Schools' dragElement()
-function works great for backwards-compatibility, because it is written for browsers of 2011 and earlier.现在, W3Schools 的
dragElement()
函数非常适合向后兼容,因为它是为 2011 年及更早版本的浏览器编写的。 Nowadays most (if not all) browsers implement HTML5, CSS3 and (at least) ES6 (the JavaScript specification), as well as certain browser standards.现在大多数(如果不是全部)浏览器都实现了 HTML5、CSS3 和(至少)ES6(JavaScript 规范)以及某些浏览器标准。
This means, the following (and more:) will be supported:这意味着,将支持以下(以及更多:):
const
/ let
.const
/ let
。var
: They declare block-scope variables, and aren't hoisted.var
之间的区别:它们声明了块范围的变量,并且没有被提升。 However, using them will make debugging easier, and will convey this message:const
implies that the reference won't ever change. const
意味着引用永远不会改变。let
implies that the reference may be changed. let
意味着引用可能会改变。Element.querySelector()
for searching descendants of an element.Element.querySelector()
搜索元素的后代。 (Similar to Document.querySelector()
.) Document.querySelector()
。)addEventListener()
/ removeEventListener()
.addEventListener()
/ removeEventListener()
。onevent
-listeners.onevent
侦听器时只能将一个事件侦听器分配给元素的问题。 Some off-topic points:一些题外话:
First off, I'll remove any pre-existing comments, so that the new comments can explain the changes.首先,我将删除任何预先存在的评论,以便新评论可以解释更改。
Some people prefer '
(single-quote) over "
(double-quote) for strings, as it is more likely that you want to use "
than '
in a string.有些人更喜欢
'
(单引号)而不是"
(双引号)作为字符串,因为您更有可能在字符串中使用"
而不是'
。 Using one allows the other to be used unescaped.使用一个可以不转义地使用另一个。
I prefer '
in JS, so don't be confused over why my changes use them.我更喜欢
'
在 JS 中,所以不要对我的更改为什么使用它们感到困惑。
I replaced all instances of var
with either const
or let
, depending on what seemed more appropriate.我用
const
或let
替换了所有var
实例,具体取决于看起来更合适的情况。
The code will now use addEventListener()
/ removeEventListener()
instead of onevent
-listener.代码现在将使用
addEventListener()
/ removeEventListener()
而不是onevent
-listener。
Most important changes:最重要的变化:
draggable
-class draggable
-class<header>
<header>
<header>
, make it a <section>
<header>
,则将其设为<section>
draggable
-classdraggable
类添加规则draggable
is also a <section>
draggable
也是<section>
xPos
, yPos
instead of pos0
, pos1
, pos2
, pos3
xPos
, yPos
代替pos0
, pos1
, pos2
, pos3
MouseEvent.movementX
(and .movementY
)MouseEvent.movementX
(和.movementY
)更新位置值Element.styles.transform
with translate()
Element.styles.transform
和translate()
更新屏幕 position // 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>
The best way I found is to set top
and left
for every element each time, and this will let every element in a separate position without breaking the graggable part:我发现最好的方法是每次为每个元素设置
top
和left
,这将让每个元素在单独的 position 中而不破坏可移动部分:
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
}
}
I modified this function to have two more parametrs to specify the top and left:我修改了这个 function 有两个参数来指定顶部和左侧:
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);
}
The function from W3Schools except the two line where(//set the element's new position:)来自 W3Schools 的 function 除了两行 where(//设置元素的新 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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.