I have a few draggable objects that are overlaying like a stack of papers.
I want each object to be lied on top of the stack when picked up.
I have been trying but somehow it is not properly working.
The closest I got was that every time I click one object I needed to click 1 extra time for at the next item until it reaches the front.
I guess its an easy fix but I fail to find it.
Thanks in advance.
dragElement(document.getElementById("drags"));
dragElement(document.getElementById("drags1"));
dragElement(document.getElementById("drags2"));
dragElement(document.getElementById("drags3"));
dragElement(document.getElementById("drags4"));
dragElement(document.getElementById("drags5"));
dragElement(document.getElementById("drags6"));
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
var z_Index = 1;
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;
elmnt.style.zIndex = z_Index;
}
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;
z_Index++;
}
}
#drags, #drags1, #drags2, #drags3, #drags4 {
position: absolute;
width: 50px;
height: 50px;
}
#drags{
background-color: red;
left:34%;
top:12%;
}
#drags1{
background-color: blue;
left:61%;
top:14%;
}
#drags2{
background-color: green;
left:22%;
top:18%;
}
#drags3{
background-color: yellow;
left:35%;
top:29%;
}
#drags4{
background-color: black;
left:40%;
top:20%;
}
<body>
<div id="drags"></div>
<div id="drags1"></div>
<div id="drags2"></div>
<div id="drags3"></div>
<div id="drags4"></div>
<div id="see-also">Footer</div>
</body>
Related
I have a draggable element. After dragging, I want to add the click-funtion again.
The click-event should not fire when dragging, but I want to add it when dragging is over.
If I add my click-event again after dragging (line 46) it get's fired immediately
clickElemtent(document.getElementById("mydiv"));
I don't understand the logic.. Thank you so much!
dragElement(document.getElementById("mydiv"));
clickElemtent(document.getElementById("mydiv"));
function clickElemtent(elmnt) {
elmnt.onclick = function() {
alert("click")
}
}
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
elmnt.onclick = null;
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement(e) {
document.onmouseup = null;
document.onmousemove = null;
e.stopImmediatePropagation();
e.stopPropagation();
clickElemtent(document.getElementById("mydiv")); ///// ?????
}
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
text-align: center;
border: 1px solid #d3d3d3;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<div id="mydiv">
<div id="mydivheader">click OR drag</div>
</div>
function closeDragElement(e) {
document.onmouseup = null;
document.onmousemove = null;
e.stopImmediatePropagation();
e.stopPropagation();
///// ?????
setTimeout(()=>
document.getElementById("mydiv").onclick = ()=>alert("click")
,100 //or set more milis
);
return true;
}
https://jsfiddle.net/t09758fb/1/
EDIT:
Becouse events like dragend click are combined of keydown, keyup - this is a key problem in Your example.
Hence JavaScript is not multithreaded - You never know which action will be fired from a handler,
but setTimeout makes Your task will probably run after other actions in surrounding code block.
mouseup and click are different events (even though both are caused by the same interaction!), so stopping one doesn't affect the other.
Event order
(An explanation to Bujaq's answer:)
An event's propagation path is determined before propagation. That means adding/removing click listeners in another click listener won't have any effect on the current event's path.
However, different event types can affect each other, even those from the same event source, e.g. a mouse click. Events are run in a fixed order; for mouse events, mouseup runs before click.
Taking the event loop into consideration: Calls to the event handlers are queued, so tasks added via setTimeout() will be run later. That is also the reason why 0 ms is enough, too.
That means, adding the onclick like that will only have an effect after the pending events have been handled.
Conditional call
You want to only accept click events if no dragging occured. That means we need a way to tell if dragging occured.
Dragging has always occured if a mousemove event happened after a mousedown event.
Since a click event is fired even when dragging, we can call the "actual" listener only when no dragging occured:
var clickHandler = function(e) {
alert("click");
};
dragElement(document.getElementById("mydiv"), clickHandler);
function dragElement(elmnt, clickCallback /*New*/) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
var isDragged = false; // New
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
if (clickCallback) {
elmnt.onclick = function(e) {
// Only call if no dragging occured
if (!isDragged) clickCallback(e);
};
}
function dragMouseDown(e) {
isDragged = false; // No dragging since now
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
isDragged = true; // Dragging occured
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement(e) {
document.onmouseup = null;
document.onmousemove = null;
}
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
text-align: center;
border: 1px solid #d3d3d3;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<div id="mydiv">
<div id="mydivheader">click OR drag</div>
</div>
Capture and stop
Alternatively to conditionally calling the click listener, we can just stop the event from propagating.
We need to stop the event before it reaches the expected listener. This can be done in two ways:
With capturing listeners.
With earlier attached listeners.
Both ways require the use of addEventListener() to attach the listeners, which is the preferred way. The onevent properties and especially inline event attributes are generally discouraged.
In the end we want to make use of the event flow. Example:
const select = document.querySelector("select");
const button = document.querySelector("button");
// Notice order of attachment
button.addEventListener("click", function stopEarly(evt) {
if (select.value === "early") evt.stopImmediatePropagation();
});
button.addEventListener("click", () => console.log("Button reached!"));
button.addEventListener("click", function stopInCapture(evt) {
if (select.value === "capture") evt.stopPropagation();
}, { capture: true });
button.addEventListener("click", function stopEarly(evt) {
if (select.value === "late") evt.stopImmediatePropagation();
}, { capture: false /*default*/ });
<select>
<option value=none>No stopping</option>
<option value=capture>Stop in capture</option>
<option value=early>Stop early</option>
<option value=late>Stop late</option>
</select>
<button>Log text</button>
If we ensure that the propagation-stopping listener is called before the "intended" listener, we don't have to remove and reattach any listeners at all:
var clickHandler = function(e) {
alert("click");
};
dragElement(document.getElementById("mydiv"), clickHandler);
function dragElement(elmnt, clickCallback /*New*/) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
var isDragged = false; // New
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
if (clickCallback) {
elmnt.addEventListener("click", function(e) {
// Stop here if dragging occured
if (isDragged) e.stopImmediatePropagation();
}, { capture: true });
elmnt.addEventListener("click", clickCallback);
}
function dragMouseDown(e) {
isDragged = false; // No dragging since now
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
isDragged = true; // Dragging occured
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement(e) {
document.onmouseup = null;
document.onmousemove = null;
}
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
text-align: center;
border: 1px solid #d3d3d3;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<div id="mydiv">
<div id="mydivheader">click OR drag</div>
</div>
Apart from the three sections above, I want to demonstrate one more thing:
Event delegation
As seen in the event flow, an event goes through multiple phases:
The capture phase.
The target phase.
The bubbling phase.
Events propagate down to and up from their target. That means we can use a single listener on an ancestor for multiple targets. This is called event delegation.
Assuming that only one element can be dragged at once, we can attach a single listener on an ancestor, where we'll stop propagation when applicable.
Under these conditions, we can make elements automatically behave as wished simply by adding a class:
const button = document.querySelector("button");
// Add *click* listener to "click or drag" button
button.addEventListener("click", () => {
console.log("Initial button");
});
// Adding new draggable buttons is simple:
const button2 = document.createElement("button");
button2.classList.add("draggable"); // For dragging functionality
button2.classList.add("blue-button"); // For styling
button2.addEventListener("click", () => console.log("Button 2!")); // The *click* listener
button2.textContent = "Click or Drag 2";
document.body.append(button2);
(function pageWideDraggable() {
let target = null;
let isDragged = false;
document.addEventListener("mousedown", evt => {
// Get draggable target and reset "dragging memory"
target = event.target.closest(".draggable");
isDragged = false;
});
document.addEventListener("mousemove", evt => {
// If draggable target exists, remember that dragging occured and update target's position
if (!target) return;
isDragged = true;
target.style.top = `${target.offsetTop + evt.movementY}px`;
target.style.left = `${target.offsetLeft + evt.movementX}px`;
});
document.addEventListener("click", evt => {
// Capture click and stop event when dragging occured
if (isDragged) evt.stopPropagation();
target = null;
}, { capture: true });
})();
.draggable {
position: absolute;
padding: 0;
cursor: pointer;
user-select: none;
}
.blue-button {
padding: 10px;
display: inline-block;
color: white;
background-color: #2196F3;
}
<button class="draggable blue-button">
click OR drag
</button>
Sidenote: Notice how the deeper elements don't need to be aware of the capturing. This allows for easy component-based development.
I want to create one image each time I click on a button and I will need to be able to drag this each image to move it where I want.
At this moment my code works only for one image.
I think I need to create my div in JS but this doesn't work..
const CreerPerso = document.getElementById('createPerso');
CreerPerso.onclick = CreationPersonnage;
async function CreationPersonnage() {
/*var divImg = document.createElement('div');
divImg.setAttribute("id", "imgPerso");
document.getElementById('body').appendChild(divImg);*/
document.getElementById("imgPerso").innerHTML = "<img src='images/circle.png' />";
//drag images Personnage
dragElement(document.getElementById("imgPerso"));
function dragElement(elmnt) {
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
if (document.getElementById(elmnt.id)) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id).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;
}
}
}
<body id="body">
<button id="createPerso" class="btn btn-primary">Créer Personnage</button>
<div id="imgPerso">
</div>
</body>
Id's in HTML need to be unique, so you should add a counter or something to the div you create and keep that id for future reference.
var personImageIdCount = 0;
async function CreationPersonnage() {
var personImageId = "imgPerso_" + (personImageIdCount++).toString();
var divImg = document.createElement('div');
divImg.setAttribute("id", personImageId);
document.getElementById('body').appendChild(divImg);
No need for async
No need to give body an ID
You pass the element, no need to get the ID to get the element
IDs need to be unique
This is simpler
function dragElement(elmnt) {
elmnt.dataset.pos1 = 0,
elmnt.dataset.pos2 = 0,
elmnt.dataset.pos3 = 0,
elmnt.dataset.pos4 = 0;
// if present, the header is where you move the DIV from:
elmnt.addEventListener("mousedown", dragMouseDown);
}
function dragMouseDown(e) {
e.preventDefault();
const elemt = e.target;
// get the mouse cursor position at startup:
elemt.dataset.pos3 = e.clientX;
elemt.dataset.pos4 = e.clientY;
}
function elementDrag(e) {
e.preventDefault();
const elemt = e.target;
if (elemt) {
// calculate the new cursor position:
let pos1 = elemt.dataset.pos3 - elemt.clientX;
let pos2 = elemt.dataset.pos4 - elemt.clientY;
let = elemt.clientX;
let = elemt.clientY;
// set the element's new position:
elemt.style.top = (elemt.offsetTop - pos2) + "px";
elemt.style.left = (elemt.offsetLeft - pos1) + "px";
}
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
const CreerPerso = document.getElementById('createPerso');
const body = document.querySelector('body');
CreerPerso.addEventListener("click", CreationPersonnage);
function CreationPersonnage() {
const divImg = document.createElement('div');
divImg.classList.add("imgPerso");
divImg.innerHTML = "<img src='images/circle.png' />";
body.appendChild(divImg);
//drag images Personnage
dragElement(divImg);
}
document.addEventListener("mouseup", closeDragElement);
document.addEventListener("mousemove", elementDrag);
.imgPerso {
position: absolute
}
<button id="createPerso" class="btn btn-primary">Créer Personnage</button>
As the title states I want the user to be able to resize an image without the user being able to drag the image around.
I have it so the user can drag the image around so they can overlay on top of each other, but I also want to add the functionality that the user can resize any of the images. I'm doing this through CSS/JS.
My sources for how to drag comes from here: https://www.w3schools.com/howto/howto_js_draggable.asp
My source for how to resize images comes from here: https://jqueryui.com/resizable/
Currently when a user tries to resize the img, the img also gets dragged around.
Example of my problem here: https://jsfiddle.net/3ybsd6rk/1/
var drag = true;
// makes any image resizable on the page
$(function() {
$("img").resizable();
});
// Make the DIV element draggable
// Simple loop that goes through all element ids that start with 'dragId'
// (This needs to be the naming convention or this won't work)
var dragPrefix = 'dragId';
var el;
for (i = 0; el = document.getElementById(dragPrefix + i); i++) {
dragElement(el);
}
function dragElement(elmnt) {
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
if (document.getElementById(elmnt.id)) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id).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;
getToTop();
}
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";
elmnt.style.opacity = "0.5"
getToTop();
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
elmnt.style.opacity = "1.0";
getToTop();
}
// Simple function that makes all other drag elements 1 and the one in 'focus' 5 which will make it
// hover over the other elements
function getToTop() {
for (i = 0; el = document.getElementById(dragPrefix + i); i++) {
el.style.zIndex = "1";
}
elmnt.style.zIndex = "5"
}
}
.drag_img {
position: absolute;
display: inline-block;
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
Right now both resizing and drag is executing at the same time causing the conflict. I think your direction on the flag is correct but resizing has a threshold so the events start and stop will be delayed compared to drag thus drag still happens first. The best option is to bind to the resizing handles so that when the user is operating the resizer the drag is disabled.
Creating a flag whenever the user's mouse is down on the handlers:
$("#mydivheader").resizable({
create: function(e, ui) {
$('.ui-resizable-handle')
.on('mousedown', function() {
resizing = true;
})
.on('mouseup', function() {
resizing = false;
});
}
});
https://jsfiddle.net/jo9m2phb/1/
I want to have multiple divs on my website to be draggable and movable across the website (or at least in the container which it's found).
I would like to clarify that I searched on the web and Stackoverflow multiple times, but all of them suggest answers by using jQuery or some other libraries. I would like a pure JavaScript way of moving multiple divs (hopefully with just one function for all) across the website.
The project that I'm making is to practice drag and drop with HTML and JS, I have 4 divs in a container 'x' (with images in them) and another container 'y' to drag and drop those divs to that container 'y', but if I drag them all there, the first div just sets to the top left corner and the rest under them (or beside each other if I set their display to inline-block). So, I want to make those divs movable on that container 'y' which I dragged into to move them around wherever I need. (They are also resizable).
The Drag and drop JavaScript and HTML code (Excluding CSS and some unnecessary HTML code) is this:
function allowDrop(ev) {
ev.preventDefault();
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
ev.dataTransfer.dropEffect = "move";
}
function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
<div id="images-container" ondrop="drop(event)" ondragover="allowDrop(event)">
<div class="resize" style="background: url('images/photo-1.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag1" draggable="true" ondragstart="drag(event)">
</div>
<div class="resize" style="background: url('images/photo-2.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag2" draggable="true" ondragstart="drag(event)">
</div>
<div class="resize" style="background: url('images/photo-3.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag3" draggable="true" ondragstart="drag(event)">
</div>
<div class="resize" style="background: url('images/photo-4.jpg') center no-repeat; background-size: cover; background-position: center center;" id="drag4" draggable="true" ondragstart="drag(event)">
</div>
</div>
<div id="gallery" ondrop="drop(event)" ondragover="allowDrop(event)"></div>
I checked this article by W3schools, but it works with just one div element, as it's built with an ID, but what can I do with this code to use it for multiple divs and with classes I guess? If there is another way of doing it, I'll be glad to hear some suggestions about it, though in pure JavaScript.
Thanks.
I guess you probably sorted this by now. I will post this here for people like me who are searching for the answer. I thought that w3Schools example would only work for a single element too. It actually works on multiple elements just by calling the function on each one.
var draggableElements = document.getElementsByClassName("draggable");
for(var i = 0; i < draggableElements.length; i++){
dragElement(draggableElements[i]);
}
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
pos3 = parseInt(e.clientX);
pos4 = parseInt(e.clientY);
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
return false;
}
function elementDrag(e) {
e = e || window.event;
pos1 = pos3 - parseInt(e.clientX);
pos2 = pos4 - parseInt(e.clientY);
pos3 = parseInt(e.clientX);
pos4 = parseInt(e.clientY);
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
console.log(elmnt.offsetTop)
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
Now at first this gave me odd results. My divs were only going in one direction when I moved the mouse, if you have margin on the divs it will mess up the calculations. So either don't use margin or edit the code to work for them.
Link to the original W3Schools tut: https://www.w3schools.com/howto/howto_js_draggable.asp
This works for me. No JQuery used.
I produces 4 divs all on top of each other but they can be dragged around.
When you click on a div, its border lights up and you can drag it. The dragged div moves in front of the other divs. Because it looks fun, I've drawn curves between the divs using a canvas.
<!doctype html>
<html>
<head>
<style>
div {
position: absolute;
top: 0;
height: 60px;
width: 100px;
background: #15a5ed;
text-align: center;
line-height: 60px;
border-radius: 15px 15px 15px 15px;
}
</style>
</head>
<body>
<canvas id="canvas" class="canvas"></canvas>
<div id="div1">Me</div>
<div id="div2">You</div>
<div id="div3">Us</div>
<div id="div4">Them</div>
<script>
divs = document.getElementsByTagName("div");
for (div of divs) div.onmousedown = onMouseDown;
document.onmousemove = onMouseMove;
document.onmouseup = onMouseUp;
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
var the_moving_div = '';
var the_last_mouse_position = { x:0, y:0 };
drawConnectors();
function onMouseDown(e) {
e.preventDefault();
the_moving_div = e.target.id; // remember which div has been selected
the_last_mouse_position.x = e.clientX; // remember where the mouse was when it was clicked
the_last_mouse_position.y = e.clientY;
e.target.style.border = "2px solid blue"; // highlight the border of the div
var divs = document.getElementsByTagName("div");
e.target.style.zIndex = divs.length; // put this div on top
var i = 1; for (div of divs) if (div.id != the_moving_div) div.style.zIndex = i++; // put all other divs behind the selected one
}
function onMouseMove(e) {
e.preventDefault();
if (the_moving_div == "") return;
var d = document.getElementById(the_moving_div);
d.style.left = d.offsetLeft + e.clientX - the_last_mouse_position.x + "px"; // move the div by however much the mouse moved
d.style.top = d.offsetTop + e.clientY - the_last_mouse_position.y + "px";
the_last_mouse_position.x = e.clientX; // remember where the mouse is now
the_last_mouse_position.y = e.clientY;
drawConnectors();
}
function onMouseUp(e) {
e.preventDefault();
if (the_moving_div == "") return;
document.getElementById(the_moving_div).style.border = "none"; // hide the border again
the_moving_div = "";
}
function drawConnectors() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.beginPath();
ctx.strokeStyle="#15a5ed";
ctx.lineWidth=3;
for (div1 of divs) for (div2 of divs) {
if (div1 == div2) continue;
ctx.moveTo(div1.offsetLeft + div1.clientWidth/2, div1.offsetTop + div1.clientHeight/2);
ctx.bezierCurveTo(div1.offsetLeft, div1.offsetTop,
div2.offsetLeft, div2.offsetTop,
div2.offsetLeft + div2.clientWidth/2, div2.offsetTop + div2.clientHeight/2);
ctx.stroke();
}
}
</script>
</body>
</html>
I have had a little play around with this. Using the w3 schools example. I have expanded on that to make it dynamic, (class selectable).
Here is the JavaScript to make it work.
document.addEventListener('DOMContentLoaded', function () {
//Make the DIV element draggagle:
members = document.querySelectorAll('.mover').forEach(item => {
item.addEventListener('mousedown', ev => {
console.log(ev);
let tid;
tid = ev.target.id;
if (ev.target.id == "") {
tid = ev.target.parentNode.id;
}
dragElement(document.getElementById(tid));
})
})
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;
}
}
});
Noting the changes I have made:
DOM Lister wrapping the code.
mousedown listener assigned to the class ".mover"
I'm trying to create a simple drag function that moves all childs inside a div tag depending on mouse movement, in simple world I calculate deltaX and deltaY and apply those to all childs inside a div by changing style.top and style.left. As regarding the X coordinate it works well while Y doesn't work (only increase) and I cannt explain why. This is what I have done
//Make the DIV element draggagle:
dragElement(document.getElementById(("mydiv")));
function dragElement(elmnt) {
var deltaX = 0, deltaY = 0, initX = 0, initY = 0;
var flag=true;
elmnt.onmousedown = dragMouseDown;
var childrens;
function dragMouseDown(e) {
e = e || window.event;
//console.log("dragMouseDown: "+e.clientX+" "+e.clientY);
childrens = document.getElementById("mydiv").querySelectorAll(".child");
// get the mouse cursor position at startup:
initX = e.clientX;
initY = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if(flag){
flag=false;
e = e || window.event;
// calculate the new cursor position:
deltaX = e.clientX-initX;
deltaY = e.clientY-initY;
console.log("deltaX: "+deltaX+" deltaY: "+deltaY);
for (var i = 0; i < childrens.length; i++) {
//console.log("childrens[i].offsetTop: "+childrens[i].offsetTop+" childrens[i].offsetLeft: "+childrens[i].offsetLeft);
childrens[i].style.top = (childrens[i].offsetTop + deltaY) + "px"; // dont work (only increase)
childrens[i].style.left = (childrens[i].offsetLeft + deltaX) + "px";
}
initX = e.clientX;
initY = e.clientY;
deltaX=0;
deltaY=0;
flag=true;
}
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
}
#mydiv {
position: fixed;
z-index: 9;
background-color: #f1f1f1;
text-align: center;
width:400px;
height:400px;
border: 1px solid #d3d3d3;
}
.child {
position: relative;
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Draggable DIV Element</h1>
<p>Click and hold the mouse button down while moving the DIV element</p>
<div id="mydiv" style="background-color:blue">
<p class="child" style="background-color:red">Move 1</p>
</div>
Try something like this:
dragElement(document.getElementById(("mydiv")));
function dragElement(elmnt) {
var deltaX = 0,
deltaY = 0,
initX = 0,
initY = 0;
var flag = true;
elmnt.onmousedown = dragMouseDown;
var childrens;
function dragMouseDown(e) {
e = e || window.event;
childrens = document.getElementById("mydiv").querySelectorAll(".child");
for (var i = 0; i < childrens.length; i++) {
childrens[i].style.top = childrens[i].style.top == "" ? "0px" : childrens[i].style.top;
childrens[i].style.left = childrens[i].style.left == "" ? "0px" : childrens[i].style.left;
}
initX = e.clientX;
initY = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (flag) {
flag = false;
e = e || window.event;
deltaX = e.clientX - initX;
deltaY = e.clientY - initY;
for (var i = 0; i < childrens.length; i++) {
childrens[i].style.top = parseInt(childrens[i].style.top) + deltaY + "px";
childrens[i].style.left = parseInt(childrens[i].style.left) + deltaX + "px";
}
initX = e.clientX;
initY = e.clientY;
flag = true;
}
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
#mydiv {
position: fixed;
z-index: 9;
background-color: #f1f1f1;
text-align: center;
width: 400px;
height: 400px;
border: 1px solid #d3d3d3;
}
.child {
position: relative;
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<h1>Draggable DIV Element</h1>
<p>Click and hold the mouse button down while moving the DIV element</p>
<div id="mydiv" style="background-color:blue">
<p class="child" style="background-color:red">Move 1</p>
<p class="child" style="background-color:yellow">Move 2</p>
</div>
This isn't a nice solution but it works. Consider using jQuery and jQuery Draggable.