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.
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().*
What I mean is that the user presses a mouse button at point xy on an HTML canvas and while the mouse button is pressed the rectangle can be resized according to the movement of the cursor with point xy fixed. Like how highlighting works.
This is what I've got so far but it doesn't seem to be working:
canvas.addEventListener('mousedown', function(e){
var rectx = e.clientX;
var recty = e.clientY;
canvas.onmousemove = function(e){
var df = e.clientX;
var fg = e.clientY;
};
context.rect(rectx, recty, df-rectx, fg-recty);
context.stroke();
}, false);
Assuming there are no transforms (scale, translate) on your canvas context.
Basic steps for having a resizable rectangle are as follows:
Create a mousedown listener that sets a flag indicating the use is holding down the mouse button, as well as sets the "anchor," or initial coordinates.
Create a mouseup listener that unsets the flag.
Create a mousemove listener that, if the flag indicates the mouse is down, redraws the canvas with the rectangle's size changed according to mouse coordinates.
An important note is that client coordinates in the event object are relative to the page, not to your canvas element. You will frequently need to convert clientX and clientY into canvas coordinates:
var getCanvasCoords = function (clientX, clientY) {
var rect = canvas.getBoundingClientRect();
return {
x: clientX - rect.left,
y: clientY - rect.top
};
};
The first two steps look something like this:
var anchorX;
var anchorY;
var mouseDown = false;
canvas.addEventListener('mousedown', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
anchorX = coords.x;
anchorY = coords.y;
mouseDown = true;
});
canvas.addEventListener('mouseup', function (event) {
mouseDown = false;
});
And the mousemove handler:
canvas.addEventListener('mousemove', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
var width = coords.x - anchorX;
var height = coords.y - anchorY;
// clear canvas for redrawing
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillRect(anchorX, anchorY, width, height);
});
Don't Render from mouse events!
The given answer is correct but it is not the best way to do this.
The are two reasons. First the mousemove event can fire up to 600+ times a second but the display only refreshes 60 times a second. Rendering from the input event is many time just a waste of CPU time as the results will be overwritten by the next mouse event before it is ever had a chance to be displayed.
The second reason is that dedicating an event listener to a single task makes it hard to add more functionality. You end up adding more and more code to the mousemove event to handle all the types of input, most of which can be ignored because of the high update speed of the mouse.
Mouse listeners
Mouse event listeners do the minimum possible. They just record the mouse state and no more. Also all mouse events return the mouse position. You should not ignore the mouse position for events like mouse down and up
The following function creates a mouse object for a element. The mouse object has the x,y position relative to the to left of the element, and the current button states for 3 buttons it is left to right button1, button2, button3.
Also when the mouse leaves the element and then releases the mouse button the mouse for the element will not see the mouseup event and not know the mouse button is up. To prevent the mouse buttons from getting stuck you turn off the buttons when the mouse leaves the element.
The best mouse listener is to the whole page as it can track mouse events that happen even when the mouse is outside the window/tab (if the window/tab has focus), but that is a little to complex for this answer.
Function to create a mouse for an element
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
mouse.x = event.pageX - bounds.left - scrollX;
mouse.y = event.pageY - bounds.top - scrollY;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
Using the mouse
It is now just a matter of creating a mouse for the element
var canMouse = createMouse(canvas);
And then in your main render loop do the dragging.
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
function mainLoop(){
if(canMouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = canMouse.x;
drag.y = canMouse.y;
drag.dragging = true;
}
drag.x1 = canMouse.x;
drag.y1 = canMouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
}
Putting it all together
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
// NOTE getting the border should not be done like this as
// it will not work in all cases.
var border = Number(element.style.border.split("px")[0])
mouse.x = event.pageX - bounds.left - scrollX - border;
mouse.y = event.pageY - bounds.top - scrollY - border;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
var mouse = createMouse(canvas);
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(mouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = mouse.x;
drag.y = mouse.y;
drag.dragging = true;
}
drag.x1 = mouse.x;
drag.y1 = mouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
if(drag.dragging){
ctx.strokeRect(drag.left, drag.top, drag.width, drag.height);
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 1px solid black;
}
Click drag to draw rectangle.<br>
<canvas id="canvas" width= "512" height = "256"></canvas>
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;
};
seems like I cannot find a solution for this. Please Help me out.
What I want to do is to have a simple volume slider.
SO, as you can see the orange part is my volume slider.
This is my jQuery:
var mouseDown = false;
$("#volSlider").mousedown(function() { mouseDown = true; });
$("#volSlider").mouseup(function() { mouseDown = false; });
$("#volSlider").mouseleave(function() { mouseDown = false; });
$("#controlVolume").mousemove(function(e)
{
if (mouseDown == true)
{
var caretFromTop = $("#volCaret").position().top;
var areaHeight = $("#volSlider").height();
var volume = (caretFromTop / areaHeight) * 100;
volume = Math.round(volume);
$("#volCaret").css("bottom", volume);
$("#volText").text(volume);
if (volume <= 100 && volume >= 0)
{
//To be added.
}
}
});
EDIT: For those who want to see my HTML:
<div id="controlVolume">
<div id="volSlider">
<div id="volCaret"></div>
</div>
<div id="volText"></div>
</div>
When i try to drag the caret to the top, it just goes to "1" and not further. Anything I am missing? Thanks in advance.
What you actually want to do is track the Y vertex of the mouse, not the height of the caret (well, technically yes - the height of the caret that changes between the mouse moves). You're currently tracking the position of the volume bar, which doesn't change.
As such your code should be something like this:
var mousePos = 0;
var mouseDown = false;
var height = 0;
$("#volSlider").mousedown(function(e) { mouseDown = true; mousePos = e.pageY; height = $("#volCaret").height(); });
$("#volSlider").mouseup(function() { mouseDown = false; mousePos = 0 });
$("#volSlider").mouseleave(function() { mouseDown = false; mousePos = 0 });
$("#controlVolume").mousemove(function(e)
{
if (mouseDown == true)
{
var areaHeight = $("#volSlider").height();
var caretHeight = height + (e.pageY - mousePos);
$("#volCaret").height(caretHeight );
var volume = caretHeight / areaHeight * 100;
console.log(volume);
}
});
It would be great if you'd put your code on jsfiddle, as probably there's something I've not thought of and this code fails horribly.