How do make a div scrollLeft based on clientX mousemove - javascript

I have a scrollable div that I want to scroll when dragging the mouse inside of it.
I have a very basic implementation in this code pen.
I can't however understand how to think of scrollLeft and clientX? I think I need 😵‍💫 to set the scrollLeft position to the clientX + the position of the scrollBar once the drag staretd. But they are somehow on different relative positions?
Resulting in a bit of "jumpy" feeling when starting to drag.
function mouseMove(event) {
if (!dragging) return;
const {scrollLeft} = initScroll;
// Even if I click exactly where the bar starts the positions are way off ...
div.scrollLeft = event.clientX + scrollLeft;
}
In this image I've just clicked at the start of the bar but the scrollLeft is at 548 and the clientX is at 282?
How can I make this a bit smoother?

You should reset the initPosition every time the mouse moves and is dragging, and instead of reassigning the scrollLeft, you can subtract from it the difference between the clientX and the initPosition:
var dragging = false;
var startX = 0;
var elem = document.querySelector('.draggable');
document.addEventListener('mousedown', (e) => (dragging = true,startX = e.clientX))
document.addEventListener('mouseup', () => (dragging = false))
document.addEventListener('mousemove', function(e) {
if (!dragging) return;
elem.scrollLeft -= e.clientX - startX;
startX = e.clientX;
})
.draggable {
overflow-y: auto;
}
.draggable .content {
width: 200%;
background-color: red;
height: 100px;
}
<div class="draggable">
<div class="content"></div>
</div>

Related

When the drag event is fired in Firefox, the mouse coordinates are (0,0)

I want to make a mobile phone theme effect on the browser, and I need to make the icon get the current mouse position when it is dragged. In order to judge whether the user wants to insert the currently dragged icon at the mouse position, but I use the event object (#drag($event) ) of the drag method in the Firefox browser to get the mouse coordinates (event. pageX, event.screenX), it shows (0,0) or a fixed value, but when I use Google Chrome, the above situation does not occur, it immediately gives me the coordinates of the current mouse. Regarding the problem of the value of layerXY in the picture, this value will only be updated once at the beginning of dragging, and will not change at the rest of the time. Since I personally like to use the Firefox browser, I want to solve this problem, can anyone help me? Or give me some other suggestions to implement this function (my English is not very good, from google translate)
You could update the mouse coordinate on a global variable when the mouse moves so that it will be ready for you when mouse is down.
let drag = document.querySelector('.note');
var pageX, pageY
drag.onmousedown = function(e) {
let coord = getCoord(drag);
let shiftX = pageX - coord.left;
let shiftY = pageY - coord.top;
drag.style.position = 'absolute';
document.body.appendChild(drag);
moveNote(e);
drag.style.zIndex = 1000;
function moveNote(e) {
drag.style.left = pageX - shiftX + 'px';
drag.style.top = pageY - shiftY + 'px';
var position = {
x: drag.style.left,
y: drag.style.top
}
}
document.onmousemove = function(e) {
moveNote(e);
};
drag.onmouseup = function() {
document.onmousemove = null;
drag.onmouseup = null;
};
}
function getCoord(elem) {
let main = elem.getBoundingClientRect();
return {
top: main.top,
left: main.left
};
}
window.onload = function() {
document.addEventListener("mousemove", function(e) {
pageX = e.pageX
pageY = e.pageY
});
drag.style.position = 'absolute';
document.body.appendChild(drag);
drag.style.display = 'block'
}
.note {
width: 50px;
height: 50px;
background: red;
display: none;
}
<div class="note"></div>

How to make drag event move in only one direction in javascript?

I'm using drag event for moving an element, but I didn't find a way to make it only move horizontally, it's there any clue for this? Thanks in advance!
There is three steps we need to do
first we get the current mouse x position
second we calculate the distance it has move and the direction by newX = oldX - e.clientX saying subtract old x with current mouse x this way we get something like -3 or 2 or -1 you see we have the distance the direction - or +
Third step we update the element horizontal position by element.style.left = (element.offsetLeft - newX) + "px"
There are more details in the comments in the code. let me know if its not clear
var oldX = 0, newX = 0; // for storing X (horizontal) positions
var element = document.getElementById("mydiv"); // The element you want to drag
// We do the dragging here
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
newX = oldX - e.clientX; // to calculate how much we have moved
oldX = e.clientX; // store current value to use for next move
element.style.left = (element.offsetLeft - newX) + "px"; // update left position
}
// we do this so there is not multiple event handlers
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
// when mouse down on element attach mouse move and mouse up for document
// so that if mouse goes outside element still drags
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
oldX = e.clientX; // store current value to use for mouse move calculation
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
element.onmousedown = dragMouseDown;
#mydiv {
position: absolute;
z-index: 9;
background-color: #2196F3;
text-align: center;
border: 1px solid #d3d3d3;
padding: 10px;
cursor: move;
color: #fff;
}
<div id="mydiv">
Click here to move
</div>

Touch drag-and-drop positioning near tap with JS

I want to make function to drag and drop some elements on my page. When I use code below, the element moves by touch, but it is not staying near finger where I tapped (for expample in the center or left/right bottom of the element), but it moves to right bottom from the finger. What I want - is to be able to move element dragging it by point where I tapped.
function touchStart(e) {
e.preventDefault();
var whichArt = e.target;
resetZ();
whichArt.style.zIndex = 10;
}
function touchMove(e) {
e.preventDefault();
var dragElem = e.target;
var touch = e.touches[0];
var positionX = touch.pageX;
var positionY = touch.pageY;
dragElem.style.left = positionX + 'px';
dragElem.style.top = positionY + 'px';
}
document.querySelector('body').addEventListener('touchstart', touchStart, false);
document.querySelector('body').addEventListener('touchmove', touchMove, false);
<!--elements I want to drag-->
<img draggable="true" id="one" src="images/one.svg" style="position: absolute; left: 50px; top: 120px; z-index: 3;">
<img draggable="true" id="two" src="images/two.svg" style="position: absolute; left: 367px; top: 150px; z-index: 3;">
I also tried to different things to calculate left and right position, but it failed, for example:
var moveOffsetX = dragElem.offsetLeft - touch.pageX;
var moveOffsetY = dragElem.offsetTop - touch.pageY;
var positionX = touch.pageX - moveOffsetX; /* + also tried*/
var positionY = touch.pageY - moveOffsetY; /* + also tried*/
So how is it possible to accomplish the right behaviour?
It happens because the anchor point of the element is on the top left corner.
Try this in your touchMove function:
dragElem.style.left = positionX - dragElem.width / 2 + 'px';
dragElem.style.top = positionY - dragElem.height / 2 + 'px';
Here is a working fiddle. You can test it on chrome using the device mode in the developer tools.
PS. you should have a touchend event to re-initialize the z-index (if you did not already do that).
Edit
So, in the touchstart event , you must save the anchor of the element (where the user touched the element).
To do so, you can use getBoundingClientRect() function. It's better than to depend on the style, top/left attributes because they may not be specified :
var elementRect = whichArt.getBoundingClientRect();
anchor = {
top: touch.pageY - elementRect.top,
left: touch.pageX - elementRect.left
};
And then you use this anchor to compute top/left instead of the center of the element (as I thought at first).
Here is the updated fiddle

drag and drop div element work well on element one, but element 2 not well

I've created a simple code to drag and drop two div elements but work well on div 1 but div 2 not work well.
The mouse cursor not Standing on div 2 correctly.
Please check the online demo link in the bottom of the question.
HTML
<div class="draggable"></div>
<div class="draggable"></div>
CSS
div.draggable {
background-color:yellowgreen;
width:150px;
height:100px;
border:1px solid #ffff66;
position:relative;
}
JavaScript
var element = null;
var elemPosL = 0;
var elemPosT = 0;
var isMouseDown = false;
document.onmousedown = function(e) {
element = e.target;
if (element.className === "draggable") {
elemPosL = e.clientX - element.offsetLeft;
elemPosT = e.clientY - element.offsetTop;
isMouseDown = true;
}
};
document.onmousemove = function(e) {
if (isMouseDown) {
element.style.left = e.clientX - elemPosL + "px";
element.style.top = e.clientY - elemPosT + "px";
}
};
document.onmouseup = function() {
isMouseDown = false;
element = null;
};
You can see online
Using position: absolute; fixes it.
If you want relative, check Implementing drag and drop on relatively positioned elements in JavaScript

How can I get the mouse coordinates relative to a parent div? Javascript

I currently have a div structured with other elements inside of it.
Something similar to below;
<div id="container" style="position: relative; width: 500px; height: 500px;">
<div style="position: absolute; left: 50px; top: 50px;"></div>
<div style="position: absolute; left: 100px; top: 100px;"></div>
</div>
I am trying to get the mouse position relative to the div with the id container.
So far I have this;
function onMousemove(event) {
x = event.offsetX;
y = event.offsetY;
};
var elem = document.getElementById("container");
elem.addEventListener("mousemove", onMousemove, false);
This works fine if the div with the id container has no children. When the container div has children it gets the mouse co-ordinates relative to the child rather than the parent.
What I mean by this is if the mouse was at a position of x: 51, y: 51 relative to the parent div, it would actually return x: 1, y: 1 relative to the child div, using the html given above.
How can I achieve what I want, no libraries please.
EDIT
tymeJV has kindly made a jsfiddle of what is happening above.
http://jsfiddle.net/N6PJu/1
The accepted answer didn't work for me in Chrome. Here's how I solved it:
function relativeCoords ( event ) {
var bounds = event.target.getBoundingClientRect();
var x = event.clientX - bounds.left;
var y = event.clientY - bounds.top;
return {x: x, y: y};
}
In essence: 'mouseposition' - 'parent element position' = 'mouseposition relative to parent element'
So here I modified your function:
function onMousemove(e){
var m_posx = 0, m_posy = 0, e_posx = 0, e_posy = 0,
obj = this;
//get mouse position on document crossbrowser
if (!e){e = window.event;}
if (e.pageX || e.pageY){
m_posx = e.pageX;
m_posy = e.pageY;
} else if (e.clientX || e.clientY){
m_posx = e.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
m_posy = e.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
//get parent element position in document
if (obj.offsetParent){
do {
e_posx += obj.offsetLeft;
e_posy += obj.offsetTop;
} while (obj = obj.offsetParent);
}
// mouse position minus elm position is mouseposition relative to element:
dbg.innerHTML = ' X Position: ' + (m_posx-e_posx)
+ ' Y Position: ' + (m_posy-e_posy);
}
var elem = document.getElementById('container');
elem.addEventListener('mousemove', onMousemove, false);
var dbg=document.getElementById('dbg'); //debut output div instead of console
Here is a working demo fiddle. As you can read in the code, this also looks at the document's scroll position.
PPK's article on 'event properties' and 'find position' are (as always) a good read!
Hope this helps!
Update:
I actually found an error in ppk's code (how rare is that?!): I corrected the erroneous var in:
if (!e) var e = window.event; to if (!e){e = window.event;}
Although the answer of Nikita Volkov is pretty solid, it might not work as expected in some cases.
I recommend using currentTarget instead of target:
const handleEvent = (event) =>
{
const bounds = event.currentTarget.getBoundingClientRect();
const x = event.clientX - bounds.left;
const y = event.clientY - bounds.top;
// Now x and y are the relative coordinates.
}
Note that:
event.target is the element that triggered the event (e.g., the user clicked on or hovered on).
event.currentTarget is the element that the event listener is attached to.
It makes the difference when you want to attach a listener to some element that has multiple children.
For example,
<div onMouseMove={handleEvent} id="div0">
<div style={{'height': 200, 'width': 200}} id="div1">
</div>
<div style={{'height': 200, 'width': 200}} id="div2">
</div>
</div>
If you use event.target and hover your mouse over div2, you will get relative coordinates regarding div2 while you might want them to be regarding div0. Then event.currentTarget will do the trick.
Html
<div id="container" style="position: relative; width: 500px; height: 500px;">
<div style="position: absolute; left: 50px; top: 50px;"></div>
<div style="position: absolute; left: 100px; top: 100px;"></div>
</div>
Javascript
function onMousemove(event) {
x = event.layerX; // position x relative to div
y = event.layerY; // position y relative to div
};
var elem = document.getElementById("container");
elem.addEventListener("mousemove", onMousemove, false);
Get the screenX and screenY properties to see where the mouse is on the screen (and possibly take the scrollHeight into account!).
Then get the left and top of the container element and calculate the offset between them: that is the position of the mouse in the element.
See https://developer.mozilla.org/en-US/docs/DOM/MouseEvent for more details.
With functional components, you can combine useRef with a click event listener (rather than needing to reference the DOM directly):
const MyElement = () => {
const ref = useRef(null);
useEffect(() => {
const currentRef = ref.current;
if (currentRef) {
const clickListener = (e) => {
const rightOfElement = currentRef.getBoundingClientRect().right;
const leftOfElement = currentRef.getBoundingClientRect().left;
const mousePosXRelativeToElement = e?.clientX ?? 0;
const mousePosX = rightOfElement - mousePosXRelativeToElement;
const fullWidth = rightOfElement - leftOfElement;
// do something, e.g. changing state based on position
if (mousePosX / fullWidth < 0.5) {
setTheme(2);
} else {
setTheme(1);
}
};
currentRef.addEventListener("click", clickListener);
return () => {
currentRef.removeEventListener("click", clickListener);
};
}
}, [setTheme]);
return (
<div className="theme-selector-wrapper">
<div className="theme-selector" ref={ref}></div>
</div>
);
};
Inside useEffect (i.e. once the element is loaded), add an event listener to the element assigned to the ref element which listens for clicks. In this example, I've changed "theme" based on the ratio of X position to the width of the element.

Categories