javascript - 将可拖动元素设置为绝对位置
问题描述
我有一些元素,我想在画布上拖动以返回它们的坐标。所以元素应该彼此相邻出现,而不是我能够拖动它们。我面临的问题是元素彼此出现,没有被空格分隔:
我正在使用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);
}
解决方案
元素出现在彼此之上,因为您#mydiv
已position: absolute
声明,这将其从文档流中取出。按照 CSS 的规定,不流动的元素将被放置在流动的元素之上。
Greg建议使用 CSS 函数translate()
来移动,而不是position: absolute
withtop
等。这将使元素留在流中,这意味着它们仍然会被考虑。
由于translate()
将在重新流动(重新验证文档的流动)后生效,即使移动可拖动元素也不会弄乱您的布局。
如果您只对结果感兴趣,请跳过。但这里有更多信息。
W3Schools (是?)臭名昭著,因为有些过时,有时甚至提出了不好的建议,所以我建议在阅读他们的文章的基础上做一些研究。
现在,W3Schools 的dragElement()
-function 非常适合向后兼容,因为它是为 2011 年及更早版本的浏览器编写的。现在大多数(如果不是全部)浏览器都实现了 HTML5、CSS3 和(至少)ES6(JavaScript 规范),以及某些浏览器标准。
这意味着,将支持以下(以及更多!):
const
/的使用let
。
它们和 之间的区别var
:它们声明了块范围的变量,并且没有被提升。但是,使用它们将使调试更容易,并传达以下信息:const
意味着引用永远不会改变。let
意味着参考可能会改变。
- 用于
Element.querySelector()
搜索元素的后代。(类似于Document.querySelector()
。) addEventListener()
/的使用removeEventListener()
。
这解决了在使用onevent
-listeners 时只能将一个事件监听器分配给元素的问题。- 使用(lambda) 箭头函数。通常可以用作功能的替代品,但它们不被提升。
- 更多语义 HTML 标签!这非常重要,因为它们的使用是(IMO)网络可访问性的主要焦点。
一些题外话:
- 我强烈建议在使用 CSS 库或类似库之前先学习编写自己的 CSS。
- 我还强烈建议学习(或至少阅读一次)所有 HTML 标记。这不仅可以帮助您理解语言,还可以让您创建可访问的页面,这对于 SEO 来说是必不可少的。
- 我还想提一下 Google、Microsoft 和其他公司为制定各种标记标准所做的努力。这也可能与 SEO 相关,但它是一大块阅读材料。
重构
首先,我将删除任何预先存在的评论,以便新评论可以解释更改。
有些人更喜欢'
(单引号)而不是"
(双引号)字符串,因为您更可能想要使用它"
而不是'
字符串。使用一个可以不转义地使用另一个。
我更喜欢'
JS,所以不要对我的更改使用它们的原因感到困惑。
var
我用const
or替换了所有实例let
,这取决于看起来更合适的情况。
代码现在将使用addEventListener()
/removeEventListener()
而不是onevent
-listener。
最重要的变化:
- 在 HTML 中:
- 可拖动元素现在使用
draggable
-class - 使用正确的标签:
- 对于标题,使用
<header>
- 如果可拖动元素包含
<header>
,则将其设为<section>
- 对于标题,使用
- 可拖动元素现在使用
- 在 CSS 中:
draggable
为-class添加规则- 为何时添加启用 CSS 规则
draggable
也是一个<section>
- 在 JS 中:
- 使用
xPos
,yPos
代替pos0
,pos1
,pos2
,pos3
MouseEvent.movementX
使用(and.movementY
)更新位置值Element.styles.transform
使用with更新屏幕位置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;
}
<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>
推荐阅读
- python - 在这个 Django 视图中,我在哪里添加所需的位置参数?
- python - 如何使用重复索引对数据框列中的值求和
- ios - App Store Info.plist 是否有大小限制?
- xml - 如何处理xquery中的空节点?
- c# - 如何让 Winforms 程序启动常规类的方法而不是启动表单?然后,如果我愿意,我想启动一个表格
- javascript - React 引导表单未按预期呈现
- javascript - 替换开始和结束分隔字符之间的所有字符
- apache-kafka - 如何修复与组协调器相关的kafka流问题不可用或无效,将尝试重新发现
- python - Python异常类型对象继承
- html - 在 Nodejs 中使用 Jquery Ajax 请求从数据库中删除