Detect hover/mouseover/mouseenter while dragging an element - javascript

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

Related

changing position relative to mouse pointer

I looked for many similar question but couldn't solve my issue. Here I have pasted only a single list item out of my code. I need to create a span on mousemove event over the list item, and set its left-offset from its parent as the mouse moves. As I haven't ever tried any such thing, I experimented a lot with the code to get to this-
document.querySelector("li a").addEventListener("mousemove",(event)=> {
let span;
let alreadyHasSpan = event.target.hasChildNodes().length > 1; //default length=1
if(!alreadyHasSpan) {
span = $(`<span class="a-effect"></span>`);
span.appendTo(event.target);
}
let targetLeftPos = event.target.getBoundingClientRect().left;
span.css("left",`${event.clientX-targetLeftPos}px`);
$(this).mouseleave(function () {
span.remove();
});
}, false);
a {
position: relative;
}
.a-effect {
position: absolute; left: 0;
height: 100%;
width: 2%;
background: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<li class="nav-item active">
Product
</li>
On my browser, the span moves but with trailing signs behind it-
Apart from the trailing signs, it oftens misses to show the span. On this question's snippets, it's even worse to show the issue.
What's the issue and how could I fix it?
As per your question, the issue is that you're adding a new span everytime the mouse moves. I am guessing you only want to add it when mouse enters, and remove it once the mouse leaves.
I have tried to keep your code as much as possible and change only what is causing your issue.
document.querySelector("li a").addEventListener("mouseenter", (event) => {
let span;
let alreadyHasSpan = event.target.childNodes.length > 1; //default length=1
if (!alreadyHasSpan) {
span = $(`<span class="a-effect"></span>`);
span.appendTo(event.target);
}
});
document.querySelector("li a").addEventListener("mousemove", (event) => {
let targetLeftPos = event.target.getBoundingClientRect().left;
if (event.target.childNodes.length > 1) {
let span = event.target.childNodes[1];
$(span).css("left", `${event.clientX-targetLeftPos}px`);
}
});
document.querySelector("li a").addEventListener("mouseleave", (event) => {
if (event.target.childNodes.length > 1) {
let span = event.target.childNodes[1];
span.remove();
}
});
a {
position: relative;
}
.a-effect {
position: absolute;
left: 0;
height: 100%;
width: 2%;
background: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<li class="nav-item active">
Product
</li>
I am not 100% sure where you want to place your span element but there are some problems in your code that i would correct first. The placement of the element is relatively easy to change then.
Firstly, you append the span element in a mousemove function and them check everytime that event triggers, if the span is already there. Change that to use the mouseenter event for the span creation.
Secondly, you add a new mouseleave handler for your link evetytime you move your mouse. Same as before, create that handler outside of your mousemove event.
Regarding the position of your elememt, I am not sure where you expect it to appear/follow your mouse but you can easily adapt the offsetX and offsetY variables in my moveSpan() function to adapt it to your needs.
Keep in mind when you are positioning the element, that the calculation is based off of the position of your a element.
function moveSpan( e ) {
let $el = $(e.target);
let xPos = e.pageX;
let yPos = e.pageY;
let offsetX = $el.width() / 2;
let offsetY = $el.height() / 2;
let $span = $el.find('span');
$span.css({
'left': (xPos - offsetX) + 'px',
'top': (yPos - offsetY) + 'px'
});
}
$('li a').on('mouseenter', function( e ) {
let span = $('<span />').addClass('a-effect');
$(this).append( span );
moveSpan( e );
}).on('mousemove', function( e ) {
moveSpan( e );
}).on('mouseleave', function( e ) {
$(this).find('span').remove();
});
ul {
list-style-type: none;
}
a {
position: relative;
}
.a-effect {
position: absolute;
height: 100%;
width: 10px;
background: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li class="nav-item active">
Product
</li>
</ul>

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!');
}
}

Issues with requestAnimationFrame

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)

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");

How to add a text box popup (Jquery tooltip or similar) to a Fabric JS image within a canvas?

I'm working on a Fabric JS project to map a floor with its rooms' locations.
At each room location I added an icon. I want to have a text box pop up (such as jquery tooltip) each time the mouse hover above the icon.
The text box should show room information (phone number \ person \ size \ etc.)
I found this google group post, but no one really described the solution beside sharing this link
Step 1: Set up your watchers
Step 2: Load the dialog
Step 3: Figure out where the bounding rect is on the page and move the dialog.
canvas.observe('mouse:over', function (e) {
console.log("Everyday I'm hovering");
showImageTools(e.target);
});
canvas.observe('mouse:out', function (e) {
$('#imageDialog').remove();
});
function showImageTools (e) {
var url = 'dialog/imageDialog.htm';
$.get(url, function(data) {
// Don't add it twice
if (!$('#imageDialog').length) {
$(body).append(data);
}
moveImageTools();
});
function moveImageTools () {
var w = $('#imageDialog').width();
var h = $('#imageDialog').height();
var e = canvas.getActiveObject();
var coords = getObjPosition(e);
// -1 because we want to be inside the selection body
var top = coords.bottom - h - 1;
var left = coords.right - w - 1;
$('#imageDialog').show();
$('#imageDialog').css({top: top, left: left});
}
function getObjPosition (e) {
// Get dimensions of object
var rect = e.getBoundingRect();
// We have the bounding box for rect... Now to get the canvas position
var offset = canvas.calcOffset();
// Do the math - offset is from $(body)
var bottom = offset._offset.top + rect.top + rect.height;
var right = offset._offset.left + rect.left + rect.width;
var left = offset._offset.left + rect.left;
var top = offset._offset.top + rect.top;
return {left: left, top: top, right: right, bottom: bottom};
}
That should be enough to get you started. Let me know if any of this doesn't make sense.
Add span element below the canvas
<span ref="toolTip" class="toolTip">ToolTip</span>
Add style for span element
NB: Visibility is hidden by default
.toolTip{
position: absolute;
z-index: 1;
background: rgb(119, 128, 0);
height: 30px;
width: 120px;
padding: 8px;
font-size: 13px;
color: #fff;
visibility: hidden;
}
Add mouse over and mouse out events
this.$data.canvas.on('mouse:over', function (e) {
// console.log(e.e.offsetX)
if (e.target && e.target.feature === 'Seat') {
self.$refs.toolTip.innerHTML =
'Seat: ' + e.target.label + ' Row: ' + e.target.rowLabel
self.$refs.toolTip.style.visibility = 'visible'
self.$refs.toolTip.style.top = e.e.offsetY + 'px'
self.$refs.toolTip.style.left = e.e.offsetX + 'px'
}
})
this.$data.canvas.on('mouse:out', function (e) {
self.$refs.toolTip.style.visibility = 'hidden'
})

Categories