Issues with requestAnimationFrame - javascript

I have an issue:
document.querySelector('div').addEventListener('mousedown', function(){
hello = true
document.body.addEventListener('mousemove', function(e){
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px'
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px'
}
})
this.addEventListener('mouseup', function(e){
hello = false
posY = Math.floor(parseInt(this.style.top))
function Fall() {
posY++
document.querySelector('div').style.top = posY + 'px'
if (posY != parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall)
}
Fall()
})
})
body {
margin:0;
position:absolute;
height:100vh;
width:100vw;
overflow:hidden
}
div {
position:absolute;
height:25px;
width:25px;
border:1px #000 solid;
bottom:0
}
div:hover {
cursor:pointer;
}
<div></div>
In this code (also on jsFiddle, when I drop the div, I want the div to fall, and stop at the ground.
The first time, it works. But then, requestAnimationFrame is faster, it's like the first one isn't done...? And after that, the div didn't stop at the ground :(
Do I have to use setInterval instead of requestAnimationFrame?

Whenever the div is clicked (mousedown) theres another listener assigned. When you stop clicking, these listeners are all executed in order, so at the second click, there will be two loops running, after the third there will be three loops running and so on. You may just assign the listener once:
var hello,posY;
document.querySelector('div').addEventListener('mousedown', function(){
hello = true;
});
document.body.addEventListener('mousemove', function(e){
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px';
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px';
}
});
document.querySelector('div').addEventListener('mouseup', function(e){
hello = false;
posY = Math.floor(parseInt(this.style.top));
function Fall() {
posY++;
document.querySelector('div').style.top = posY + 'px';
if (posY < parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall);
}
Fall();
});
And please always end a statement with a semicolon...

The problem is that you're adding your mouseup handler every time the div receives a mousedown. You only want to do that once, so move that out of your mousedown handler. So you set up the animation twice on the second call (because both handlers respond), three times on the third (because all three handlers respond), etc. And since multiple handlers are updating poxY, it doesn't stop at the ground anymore because the != check fails for all but one of them. (See further notes under the snippet.)
document.querySelector('div').addEventListener('mousedown', function() {
hello = true
document.body.addEventListener('mousemove', function(e) {
if (hello) {
document.querySelector('div').style.left = (e.clientX - 12.5) + 'px'
document.querySelector('div').style.top = (e.clientY - 12.5) + 'px'
}
})
})
// Moved the below
document.querySelector('div').addEventListener('mouseup', function(e) {
hello = false
posY = Math.floor(parseInt(this.style.top))
function Fall() {
posY++
document.querySelector('div').style.top = posY + 'px'
if (posY != parseInt(window.innerHeight) - 25) requestAnimationFrame(Fall)
}
Fall()
})
body {
margin: 0;
position: absolute;
height: 100vh;
width: 100vw;
overflow: hidden
}
div {
position: absolute;
height: 25px;
width: 25px;
border: 1px #000 solid;
bottom: 0
}
div:hover {
cursor: pointer;
}
<div class="square"></div>
Some other observations:
Your code is falling prey to The Horror of Implicit Globals* — declare your variables
Rather than re-querying the DOM all the time, it would probably be best to query the DOM for the div once, and remember it in a variable
* (disclosure: that's a post on my anemic little blog)

Related

How to console log when offsetLeft reach a number of pixels

I'm creating a simple game: drag en element far enough (200px) The console logs fine, when I drag the element 200px the console reads 200px, but I still get the wrong message.(not there yet)..
function far_enough() {
console.log('You have moved the box ' + el.offsetLeft + 'pixels');
if(el.offsetleft == 200){
console.log('200px great!');
} else {
console.log('not there yet!');
}
}
}
To answer your question, element.offsetLeft can return a decimal value. This means that it may return 200.01px instead of 200px exactly. This is what's causing your code to not work. A simple work-around is simply to use Math.round(element.offsetLeft) to return an integer value. However, even when using this, there's a chance that the offsetLeft does not return 200px exactly, especially when you drag the element too fast (the browser does not repaint for each pixel moved). Another solution is to use a range like from 200px to 250px.
I suppose that you're using position: absolute and manipulating left CSS property to cause the element to be draggable. However, moving elements with transform is better than using position. I highly suggest reading this article on why.
As you're moving your element using transform now, the offsetLeft value of the element never changes. Therefore, you can alternatively get the rendered box position using getBoundingClientRect(). Here's an example using a getBoundingClientRect(), which returns a left value relative to the viewport.
const draggable = document.querySelector('#draggable')
function farEnough() {
const box = draggable.getBoundingClientRect()
// Get center of box; move center of box by 200px to return true
let center = box.left + (box.right - box.left) / 2
if (center >= 200 && center <= 300) return true
else return false
}
let dragging = false
let dragStartX = null
let dragStartY = null
let draggableMovedX = null
let draggableMovedY = null
function dragHandler(e) {
dragging = true
dragStartX = e.clientX
dragStartY = e.clientY
}
function moveHandler(e) {
if (dragging) {
let moveX = e.clientX - dragStartX + draggableMovedX
let moveY = e.clientY - dragStartY + draggableMovedY
draggable.style.transform = `translate(${moveX}px, ${moveY}px)`
if (farEnough()) {
draggable.removeEventListener('mousedown', dragHandler)
window.removeEventListener('mousemove', moveHandler)
window.removeEventListener('mouseup', leaveHandler)
dragging = false
dragStartX = null
dragStartY = null
// 175px so that the center of the box is exactly at 200px from the left
draggable.style.transform = `translate(175px, ${moveY}px)`
console.log('You did it! You moved it 200px to the right!')
}
}
}
function leaveHandler(e) {
dragging = false
dragStartX = null
dragStartY = null
const box = draggable.getBoundingClientRect()
draggableMovedX = box.left
draggableMovedY = box.top
}
draggable.addEventListener('mousedown', dragHandler)
window.addEventListener('mousemove', moveHandler)
window.addEventListener('mouseup', leaveHandler)
body,
html {
width: 100%;
height: 100%;
position: relative;
margin: 0;
}
#draggable {
width: 50px;
height: 50px;
border-radius: 5px;
background: #121212;
cursor: grab;
}
#draggable:active {
cursor: grabbing;
}
#line {
position: absolute;
top: 0;
left: 200px;
width: 2px;
height: 100%;
background: red;
transform: translateX(-50%);
}
<div id="draggable"></div>
<div id="line"></div>
try
function far_enough()
{
let leftOffset = el.offsetLeft;
console.log('You have moved the box ' + leftOffset + 'pixels');
if (leftOffset == 200)
{
console.log('200px great!');
}
else
{
console.log('not there yet!');
}
}

Detect hover/mouseover/mouseenter while dragging an element

How to detect hover/mouseover/mouseenter while dragging an element? I want to have green box after hovering it with the "drag" element. Is there any solution for that?
Note: I know that I could use jQuery UI for it but I want to do it by myself.
$("box").mouseover(function() {
$(this).addClass("green");
var box = $(this).attr("id");
$("#result").html(box);
});
$("box").mouseleave(function() {
$(this).removeClass("green");
});
$("drag").bind({
mousedown: function() {
$(this).addClass("absolute");
},
mouseup: function() {
$(this).removeClass("absolute");
},
mousemove: function(e) {
$(this).css({
left: e.pageX - (50 / 2),
top: e.pageY - (50 / 2)
});
}
});
$("body").mousemove(function(event) {
$("#log").text("pageX: " + event.pageX + ", pageY: " + event.pageY);
});
https://jsfiddle.net/38zecoL1/1/
Thank you for any help.
I would try to disable pointer events on the dragged object using: pointer-events: none;. That way you should get the events of the hovered objects instead of the dragged.
But you also need to adapt to the situation that the move and mouseup event will not work. You will have to bind them elsewhere (body for example)
This short example is not perfect but schuld give you a hint of how to do it better ;)
$("box").mouseover(function () {
$(this).addClass("green");
var box = $(this).attr("id");
$("#result").html(box);
});
$("box").mouseleave(function () {
$(this).removeClass("green");
});
$("#drag").bind({
mousedown : function (e) {
$(document.body).css({ 'user-select': 'none' })
var dragged = $(this);
dragged.css({
left : e.pageX - (50 / 2),
top : e.pageY - (50 / 2)
});
dragged.css({
'pointer-events' : 'none'
})
var upHandler = function () {
dragged.removeClass("absolute");
dragged.css({
'pointer-events' : 'all'
})
$(document.body).css({ 'user-select': 'initial' })
$("body").off('mouseup', upHandler);
$("body").off('mousemove', moveHandler);
}
var moveHandler = function (e) {
dragged.addClass("absolute");
dragged.css({
left : e.pageX - (50 / 2),
top : e.pageY - (50 / 2)
});
}
$("body").bind({
mouseup : upHandler,
mousemove : moveHandler
})
}
});
$("body").mousemove(function (event) {
$("#log").text("pageX: " + event.pageX + ", pageY: " + event.pageY);
});
box {
float: left;
width: 100px;
height: 100px;
background-color: red;
margin: 20px;
}
#log {
position: absolute;
top: 150px;
}
.green {
background-color: green;
}
#drag {
width: 50px;
height: 50px;
float: left;
background-color: blue;
}
#drag.absolute {
position: absolute;
}
html,
body {
width: 100%;
height: 100%;
}
* {
margin: 0;
padding: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<box id="box1">
<div id="drag"></div>
</box>
<box id="box2"></box>
<div id="result"></div>
<div id="log"></div>
The reason the container stays green and the other one doesn't change is that the element you're dragging is a child of the first container. So while your mouse is in the blue draggable box it's still considered inside the container on the left because the blue box is a child of the first container.
One way to fix this (and most likely isn't the best way) is to keep track of where the mouse is on the screen (which you're already doing to move the blue block). In there if you add a bit of code checking if the mouse is within the bounding box of either of the other containers and add/remove classes based on that. Then the classes will be added based on mouse position and not whether the mouse is over an element that is a child or is not a child.
Example: https://jsfiddle.net/38zecoL1/3/
var boxes = $("box")
for(var i = 0; i < boxes.length; i++){
var boundingBox = boxes[i].getBoundingClientRect();
if(e.pageX < boundingBox.right &&
e.pageX > boundingBox.left &&
e.pageY < boundingBox.bottom &&
e.pageY > boundingBox.top){
$(boxes[i]).addClass("green");
} else{
$(boxes[i]).removeClass("green");
}
}
This is likely pretty expensive to add in a page that deals with a more complex page than just a few divs and may not perform well in those more complex situations.
If you want to drag element I recommend you to use this JS library https://github.com/RubaXa/Sortable
There is an opt called
chosenClass: "sortable-chosen", // Class name for the chosen item
and in this class you can add different color and everything you want.
But if you wanto to do this by yourself i don't now

how to move one div with another in javascript

I am trying to make a web app with two boxes, one contained in the other. The user should be able to click and move the inner box, however, the user should not be able to move this box outside the confines of the outer box. The user can move the outer box by dragging the inner box against one of the edges of the outer box. I know how to move the inner box, but the problem is how to move the other box with this restriction. Can anybody help me please? Here is what I did so far:
<!doctype html>
<head>
<title>JavaScript Game</title>
<style>
#container {
height:400px;
width:600px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:green;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:red;
left: 200px;
top: 200px;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="guy"></div>
<script>
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
lastX = event.pageX;
lastY = event.pageY;
}
}
</script>
</body>
You could add a check to see if moving the box would break bounds of cont.
try to use getBoundingClientRect()
Check the snippet below for the working code.
View in full screen for best results.
var guy=document.getElementById("guy");
var cont=document.getElementById("container");
var lastX,lastY; // Tracks the last observed mouse X and Y position
guy.addEventListener("mousedown", function(event) {
if (event.which == 1) {
lastX = event.pageX;
lastY = event.pageY;
addEventListener("mousemove", moved);
event.preventDefault(); // Prevent selection
}
});
function buttonPressed(event) {
if (event.buttons == null)
return event.which != 0;
else
return event.buttons != 0;
}
function moved(event) {
if (!buttonPressed(event)) {
removeEventListener("mousemove", moved);
} else {
var distX = event.pageX - lastX;
var distY = event.pageY - lastY;
guy.style.left =guy.offsetLeft + distX + "px";
guy.style.top = guy.offsetTop + distY + "px";
// ********************************************************************
// get bounding box borders
var contBounds = guy.getBoundingClientRect();
var guyBounds = cont.getBoundingClientRect();
// check bottom bounds
if (contBounds.bottom >= guyBounds.bottom){
cont.style.top = cont.offsetTop + distY + "px";
}
// check top bounds
if (contBounds.top <= guyBounds.top){
cont.style.top = cont.offsetTop + distY + "px";
}
// check left bounds
if (contBounds.left <= guyBounds.left){
cont.style.left = cont.offsetLeft + distX + "px";
}
// check right bounds
if (contBounds.right >= guyBounds.right){
cont.style.left = cont.offsetLeft + distX + "px";
}
// ********************************************************************
lastX = event.pageX;
lastY = event.pageY;
}
}
#container {
height:300px;
width:300px;
outline: 1px solid black;
position:absolute;
left:50px;
top: 0px;
background-color:#CCC;
}
#guy {
position:absolute;
height:50px;
width:50px;
outline: 1px solid black;
background-color:#000;
left: 200px;
top: 200px;
}
<div id="container"></div>
<div id="guy"></div>
try this link to get you started as far as keeping the "guy" inside the "contatiner": http://www.w3schools.com/html/html5_draganddrop.asp
their example shows how you can make an element only drop inside another element.
as far as moving the container...i would think that you could add some if else statements into the moved function that will test the position of the guy against the conatiner's outline and say that when they meet to move the container as well.
i am very new to javascript myself but this is just a suggestion from what i think i understand of it.

How do I position a div relative to the mouse pointer exactly when scroll page?

I found this example on my search.
But it is useless, because when the webpage has long height, and my <div> block isn't on the top, when I scroll the page, there are different distances with different PageY or clientY, so the movable <div> can not exactly go after the mouse cursor.
Here's what I've tried so far:
jQuery("#requestStatusChart").mouseover(function (event) {
var maskVal = '<span id="floatTip" style="position:absolute"><span id="hintdivlogistics" class="RMAHintdivlogistics">' +
+'</span><div class="clear"></div></span>';
jQuery(this).find(".DashboardMask").append(maskVal)
ShowHintInfoLogistics("abc");
//when onmouse out ,remove the elements I appended before.
jQuery(this).find(".DashboardMask").mouseout(function () {
if (typeof jQuery("#hintdivlogistics") != undefined) {
jQuery("#floatTip").fadeOut("slow").remove();
}
});
//move current row
jQuery(this).find(".DashboardMask").mousemove(function (event) {
_xx = event.clientX;
_yy = event.clientY;
_yyPage = event.pageY;
var pos = jQuery(this).position();
console.log((pos.left + " " + pos.top));
jQuery("#floatTip").css({ "top": _yy + "px", "left": _xx - 180 + "px",
"border": "2px solid red"
}).fadeIn("slow");
console.log("x:" + _xx + ",y:" + _yy / _yyPage * _yy);
return false;
});
return false;
});
I don't know of any way to do that reliably, given that you don't know the position of the mouse without a mouse event. You could keep track of the mouse position on mousemove, but as this snippet demonstrates it's far from ideal.
function mousemoved(event) {
var f = document.querySelector('#floater');
console.log(event);
f.style.top = event.pageY + f.scrollTop + 'px';
f.style.left = event.pageX + 'px';
}
document.querySelector('#container').addEventListener('mousemove', mousemoved);
#container {
overflow: scroll;
position: relative;
}
#content {
height: 4000px;
background: lightblue;
}
#floater {
position: absolute;
border: 1px solid black;
padding: 1em 2em;
}
<div id="container">
<div id="floater">Hi</div>
<div id="content">content just to make the container taller</div>
</div>
I have solved this problem use another way.
in X axis we can do like this.
content means your main program width,codes adapted all resolution.
var widthContent = jQuery("#content").width();
jQuery("#floatTip").css("left", _xx - (window.screen.width - widthContent)/2 + "px");

Scrolling function in JS marginTop is not changing

I have a function where I am trying to create a scrolling effect on the div's content by using an event for mouse wheel.
The listener:
document.getElementById('menu_base').addEventListener("mousewheel", scrollfunc, false);
I have this for my scroll event:
function scrollfunc(e){
console.log(e);
if(e.wheelDeltaY == 120) { //this does work
document.getElementById('menu_base').style.marginTop += 50; //no change
} else if(e.wheelDeltaY == -120){ //as does this
document.getElementById('menu_base').style.marginTop -= 50; //no change
}
}
The console.log shows:
WheelEvent {webkitDirectionInvertedFromDevice: false, wheelDeltaY: -120, wheelDeltaX: 0, wheelDelta: -120, webkitMovementY: 0…}
menu_base properties are:
.menu_base{
width:100%;
height:600px;
position:absolute;
left: 0;
top: 0;
text-align:center;
border:1px solid black;
overflow:hidden;
}
Yet the content does not move, marginTop does not seem to alter.
Any suggestions on what might be the probable cause ?
You need to define the marginTop with an unit, e.g. using px:
var menu_base = document.getElementById('menu_base');
function scrollfunc(e) {
console.log(e);
var mtop = parseInt(menu_base.style.marginTop, 10) || 0;
if (e.wheelDeltaY == 120) { //this does work
mtop += 50; //no change
} else if (e.wheelDeltaY == -120) { //as does this
mtop -= 50; //no change
}
menu_base.style.marginTop = mtop + 'px';
}
Fiddle

Categories