My problem is when I add a mousemove listener on document, all divs are moving but when I add a mousemove listener on my element, I have to move the cursor slowly.
Here is my code :
let componentsItems = document.getElementsByClassName("componentItem");
[].forEach.call(componentsItems, function (componentItem) {
componentItem.addEventListener("click", function (event) {
let selectedComponent = getComponentToDisplay(event.target.getAttribute("data-exchange"));
let mapContainer = document.getElementById("mapContainer");
let mainElement = document.createElement("div");
mainElement.innerHTML = "test";
mainElement.style.position = "absolute";
mapContainer.appendChild(mainElement);
mainElement.addEventListener("mouseup", function (e) {
isDown = false;
});
mainElement.addEventListener("mousedown", function (e) {
isDown = true;
offset = [
mainElement.offsetLeft - e.clientX,
mainElement.offsetTop - e.clientY
];
});
document.addEventListener("mousemove", function (e) {
e.preventDefault();
mousePosition = {
x: e.clientX,
y: e.clientY
};
let left = (mousePosition.x + offset[0]);
let top = (mousePosition.y + offset[1]);
if(isDown){
if(mapContainer.offsetTop < top && mapContainer.offsetWidth > left){
mainElement.style.left = left + 'px';
mainElement.style.top = top + 'px';
}
}
});
});
});
For each component in my menu, I add an onclick listener to appendChild element in the "MapContainer" div.
The drag and drop problem.
The problem is you are attaching multiple mousemove listeners to document, and each one with every one of the different mainElements.
The solution:
Remember which element we are about to move.
mainElement.addEventListener("mousedown", function (e) {
isDown = true;
element = mainElement;
offset = [
mainElement.offsetLeft - e.clientX,
mainElement.offsetTop - e.clientY
];
});
On the outter scope (outside foreach) create a unique mousemove event listener, and update the element that we mousedowned before.
document.addEventListener("mousemove", function (e) {
e.preventDefault();
mousePosition = {
x: e.clientX,
y: e.clientY
};
let left = (mousePosition.x + offset[0]);
let top = (mousePosition.y + offset[1]);
if(isDown){
if(mapContainer.offsetTop < top && mapContainer.offsetWidth > left){
element.style.left = left + 'px';
element.style.top = top + 'px';
}
}
});
Other ways to solve this problem is to create (and delete) the eventlistener on the mousedown and mouseup event handlers respectively. But I believe it's less efficient and definitely more complicated.
Related
How to set and use the local storage data of the left sidebar width after resizing the sidebar and reloading the page?
I have created a local storage data and retrieve it using the codes below but after reloading the page the resized sidebar is going back to its default width. It should have an "onload" event attribute?
Here is the link where I get the codes https://htmldom.dev/create-resizable-split-views/
Credit to: htmldom.dev for sharing this code
document.addEventListener('DOMContentLoaded', function () {
// Query the element
const resize = document.getElementById('dragMe');
const leftSide = resize.previousElementSibling;
const rightSide = resize.nextElementSibling;
// The current position of mouse
let x = 0;
let y = 0;
let leftWidth = 0;
// Handle the mousedown event
// that's triggered when user drags the resize
const mouseDownHandler = function (e) {
// Get the current mouse position
x = e.clientX;
y = e.clientY;
leftWidth = leftSide.getBoundingClientRect().width;
// Attach the listeners to `document`
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
};
const mouseMoveHandler = function (e) {
// How far the mouse has been moved
const dx = e.clientX - x;
const dy = e.clientY - y;
// Set a new left width and saving to local storage
const newLeftWidth = ((leftWidth + dx) * 100) / resize.parentNode.getBoundingClientRect().width;
leftSide.style.width = `${newLeftWidth}%`;
resize.style.cursor = 'col-resize';
document.body.style.cursor = 'col-resize';
leftSide.style.userSelect = 'none';
leftSide.style.pointerEvents = 'none';
localStorage.setItem('newLeftWidth', leftSide.style.width);
const localNewLeftWidth = localStorage.getItem('newLeftWidth');
leftSide.style.width = localNewLeftWidth;
console.log('log:' + localNewLeftWidth);
rightSide.style.userSelect = 'none';
rightSide.style.pointerEvents = 'none';
};
const mouseUpHandler = function () {
resize.style.removeProperty('cursor');
document.body.style.removeProperty('cursor');
leftSide.style.removeProperty('user-select');
leftSide.style.removeProperty('pointer-events');
rightSide.style.removeProperty('user-select');
rightSide.style.removeProperty('pointer-events');
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
};
// Attach the handler
resize.addEventListener('mousedown', mouseDownHandler);
});
If you want to load the stored position when the page is loaded, you might want to use the DOMContentLoaded event:
document.addEventListener("DOMContentLoaded", () => {
const localNewLeftWidth = localStorage.getItem('newLeftWidth');
leftSide.style.width = `${localNewLeftWidth}%`;
console.log('log: ' +`${localNewLeftWidth}%`);
// Possibly load other stuff here
});
Note that you have to add this event listener outside of your mouseMoveHandler.
You can learn more about the DOMContentLoaded event here: https://developer.mozilla.org/de/docs/Web/API/Window/DOMContentLoaded_event.
I'm using a drag function (not jquery UI, I don't want Jquery UI) on a div with a class, but I have more div with the same class, How can I intercept the click on this div
Here my code:
$(function(){
var dragging = false;
var iX, iY;
// how can I intercept $(this, ".marker") ??
$(".marker").mousedown(function(e) {
dragging = true;
iX = e.clientX - this.offsetLeft;
iY = e.clientY - this.offsetTop;
this.setCapture && this.setCapture();
return false;
});
document.onmousemove = function(e) {
if (dragging) {
var e = e || window.event;
var oX = e.clientX - iX;
var oY = e.clientY - iY;
// how can I find the ID related to this div ?
$('.marker').css({"left":oX + "px", "top":oY + "px"});
return false;
}
};
$(document).mouseup(function(e) {
dragging = false;
// $(".marker")[0].releaseCapture();
e.cancelBubble = true;
})
})
And here the original codePen https://codepen.io/Jonh/pen/jgyLB
Here the JS that create the div, they are created by JS dynamically
for (i = 0; i < theFeat.length; i++) {
markerText = theFeat[i].value;
mk_totalHeight += 50;
$('<div>', { id: 'd' + i, style:"top:" + mk_totalHeight + "px;"}).addClass('marker').appendTo('#map');
$('<div>', { id: i, style:"background-image:url('../../../symbols/marker" + (i + 1) + ".png');" }).addClass('markerIcon').appendTo('#d'+ i);
$('<span>', { id: 's' + i, text: markerText}).appendTo('#d' + i);
} // end for loop
It's useful if you have just one div but how can I implement that on a class that is on a various div, I have to copy the function and add as a select the ID? (the max allowed div are 4, so it will be #d0, #d1, #d2, #d3)
So one of your listeners,
// how can I intercept $(this, ".marker") ??
$(".marker").mousedown(function(e) {
dragging = true;
iX = e.clientX - this.offsetLeft;
iY = e.clientY - this.offsetTop;
this.setCapture && this.setCapture();
return false;
});
...won't, in fact, work as you expect. Because the .marker elements are being created dynamically, event listeners on them will fail. Rather, listen at the nearest parent node:
// Try something like this:
$("#map").on("mousedown", ".marker", function(e){
// Either of the following should get to the element that
// triggered the mousedown
var target = $(this);
// OR YOU CAN USE
var target = $(e.currentTarget);
// ... whatever other processing you need...
});
After the comment of #Snowmonkey I notice that the problem was on document.onmousemove where do you change the CSS property, this is my solution.
I created a var to get the selected div
$(".marker").mousedown(function(e) {
dragging = true;
mdiv = $(this);
iX = e.clientX - this.offsetLeft;
iY = e.clientY - this.offsetTop;
this.setCapture && this.setCapture();
return false;
});
Then on the onmousemove function, I have the target stored on a var and I can get the ID
document.onmousemove = function(e) {
if (dragging) {
var e = e || window.event;
var oX = e.clientX - iX;
var oY = e.clientY - iY;
$("#" + mdiv.attr('id')).css({"left":oX + "px", "top":oY + "px"});
return false;
}
};
When the content goes outside the div, we use scrollbars to see it. How can I scroll the div content by grabbing and dragging its background? I've searched the solution but did not find what I need. Here is my fiddle:
https://jsfiddle.net/vaxobasilidze/xhn49e1j/
Drag any item to the right div and move it outside the container to the right or bottom. scrollbars appear to help you to scroll. Here is an example of what I want to achieve. See the first diagram on the link and drag it:
https://jsplumbtoolkit.com/
Any tips on how to do this?
You should just need to detect when the mouse is down and then when the mouse is moving afterwards you can store the previous mouse coordinates and reference the current coordinates. Finally you can scroll the div in question by an amount based on the difference in drag since the last mousemove call.
var mouseDown = false;
var prevCoords = { x: 0, y: 0 };
$("#mainDiv").mousedown(function() {
mouseDown = true;
}).mousemove(function(e) {
var currentScrollX = $('#mainDiv').scrollLeft();
var currentScrollY = $('#mainDiv').scrollTop();
if(mouseDown) {
$('#mainDiv').scrollLeft(currentScrollX + prevCoords.x - (e.clientX + currentScrollX))
$('#mainDiv').scrollTop(currentScrollY + prevCoords.y - e.clientY)
};
prevCoords.x = e.clientX + currentScrollX;
prevCoords.y = e.clientY;
}).mouseup(function() {
mouseDown = false;
});
https://jsfiddle.net/6rx30muh/
EDIT: Fixed bug with wiggling tables when dragging:
var mouseDown = false;
var prevCoords = { x: 0, y: 0 };
$("#mainDiv").mousedown(function() {
mouseDown = true;
}).mousemove(function(e) {
var currentScrollX = $('#mainDiv').scrollLeft();
var currentScrollY = $('#mainDiv').scrollTop();
if(mouseDown) {
$('#mainDiv').scrollLeft(currentScrollX + prevCoords.x - e.clientX)
$('#mainDiv').scrollTop(currentScrollY + prevCoords.y - e.clientY)
};
prevCoords.x = e.clientX;
prevCoords.y = e.clientY;
}).mouseup(function() {
mouseDown = false;
});
Check for mousemove between mousedown and mouseup on the body element is a good place to start.
element = $('body');
element.addEventListener("mousedown", function(){
flag = 0;
}, false);
element.addEventListener("mousemove", function(){
flag = 1;
}, false);
element.addEventListener("mouseup", function(){
if(flag === 0){
console.log("click");
}
else if(flag === 1){
console.log("drag");
}
}, false);
I'm currently trying to create my own drag and drop function in javascript (with events listeners) and i'm facing one problem : when I start dragging my element, I can't get to know where I am since the element im dragging is in front of my cursor.
I'd like to know if it's possible to get the position of my cursor without being affected by the element im dragging.
Note : I can't use Jquery to acheive this
Thanks,
Edit : My code
var i = 0;
window.addEventListener('mousedown', startDrag);
var leftMarg = document.getElementById('moving').offsetWidth / 2;
var topMarg = document.getElementById('moving').offsetHeight / 2;
function stopDrag(e) {
window.removeEventListener('mousemove', dragging);
window.removeEventListener('mouseup', stopDrag);
window.removeEventListener('mousemove', grabbing);
}
function startDrag() {
window.addEventListener('mouseup', stopDrag);
window.addEventListener('mousemove', grabbing);
}
function grabbing(e) {
for (let i = 1; i < document.getElementById("test").children.length; i++) {
if (document.getElementById("test").children[i].contains(e.target)) {
document.getElementById("moving").style.visibility = "visible";
document.getElementById("moving").innerText = e.target.innerText;
window.removeEventListener('mousemove', grabbing);
window.addEventListener('mousemove', dragging);
}
}
}
function dragging(e) {
document.getElementById("moving").style.left = (e.x - leftMarg) + "px";
document.getElementById("moving").style.top = (e.y - topMarg) + "px";
}
when listening to the onmousemove event you can get the mouse position with event.clientX/Y. Examples are here: https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onmousemove (contains also Drag and Drop example) and here: https://jsfiddle.net/qf4atst5/
You need to calculate clientX/Y - element's left/topOffset to get the mouse position relative to your element.:
element.addEventListener("mousemove", function(e) {
xpos = e.clientX - e.target.getBoundingClientRect().top;
ypos = e.clientY - e.target.getBoundingClientRect().left;
element.innerText = "x=" + xpos + ", y=" + ypos;
});
No jQuery required
PS: To get your mouse position remove the e.target.getBoundingClientRect().*
I making a simple drag'n'drop interface. I have a bunch of containers ("wrapper") and some dynamically added items ("dragElement") in one of them. So I need, when I move item over another container, JS detect it and move the item there when the drag is finished.
I tried to detect container with "onmouseover" and "mouseup" when dragging item, but had no success, because, actually, mouse always was over the dragged element.
So how can I detect container when drag item? In pure JS please...
document.onmousedown = function(e) {
var dragElement = e.target;
if (!dragElement.classList.contains('draggable')) return;
var coords, shiftX, shiftY, detectPage;
startDrag(e.clientX, e.clientY);
document.onmousemove = function(e) {
moveAt(e.clientX, e.clientY);
};
wrapper.onmouseover = function(e) {
detectPage = e.target;
console.log(detectPage);
};
dragElement.onmouseup = function() {
finishDrag();
};
function startDrag(clientX, clientY) {
shiftX = clientX - dragElement.getBoundingClientRect().left;
shiftY = clientY - dragElement.getBoundingClientRect().top;
dragElement.style.position = 'fixed';
document.body.appendChild(dragElement);
moveAt(clientX, clientY);
};
function finishDrag() {
dragElement.style.top = parseInt(dragElement.style.top) - wrapper.getBoundingClientRect().top + 'px';
dragElement.style.position = 'absolute';
wrapper.onmouseup = function(e) {
var selectPage = e.target;
}
wrapper.appendChild(dragElement);
document.onmousemove = null;
dragElement.onmouseup = null;
};
function moveAt(clientX, clientY) {
var newX = clientX - shiftX;
var newY = clientY - shiftY;
if (newX < 0) newX = 0;
if (newX > wrapper.offsetWidth - dragElement.offsetWidth) {
newX = wrapper.offsetWidth - dragElement.offsetWidth;
}
dragElement.style.left = newX + 'px';
dragElement.style.top = newY + 'px';
};
return false;
};
Well, no one help. So one free day gone to find the solution. All I can found is to delete function finishDrag() from dragElement.onmouseup and change it to the code below.
If in shorter, when onmouseup comes, dragElement must go to display:none and now we can get access to the object near the mouse cursor through elementFromPoint. When we done with it, we can easily detects container, bring an element back to display:block and put it to that container...
Hope, it helps to someone...
dragElement.onmouseup = function(e) {
dragElement.style.display = 'none';
var selectPage = document.elementFromPoint(e.clientX, e.clientY);
dragElement.style.display = 'block';
dragElement.style.top = parseInt(dragElement.style.top) - selectPage.getBoundingClientRect().top + 'px';
dragElement.style.position = 'absolute';
selectPage.appendChild(dragElement);
document.onmousemove = null;
dragElement.onmouseup = null;
};