Change cursor while moving mouse - javascript

I'm having a strange error with trying to put a "moving" class on an element when moving/dragging the mouse. I'm using jQuery 3.1.1 on Chrome 59.0.3071.115.
I've simplified my problem down to this example:
<html>
<head>
<style>
.thing {
display: block;
width: 10em;
height: 10em;
background: green;
}
.moving {
cursor: move;
}
</style>
<script src="jquery-3.1.1.min.js"></script>
</head>
<body>
<div class="thing"></div>
<script>
$(document).ready(function(){
var $thing = $('.thing');
$thing.on('mousedown', function(e){
$thing.addClass("moving");
console.log("mousedown");
}).on('mouseup', function(e){
$thing.removeClass("moving");
console.log("mouseup");
});
});
</script>
</body>
</html>
This will display a green box in the page, and fires events when you mouse-down and mouse-up on it.
What happens is...
Click the green box -- The "moving" class gets applied to the div (this can be seen in the Chrome Developer Tools: Elements tab), but the cursor stays the usual arrow. I would expect the cursor to change to the move cursor.
While holding down the click, drag a bit -- The cursor still remains the default arrow.
Release the click while on the green div -- The cursor switches to the move cursor for a moment, but switches back to the default arrow if the mouse is moved at all.
I've tried solutions like https://stackoverflow.com/a/16172027/1766230, and others, without luck. I've tried various combinations of selectors in the CSS, various elements, etc. Strangely when attempting this in jsfiddle everything works correct, but with this content as a stand-alone HTML file, I see the error.
Edit
Turns out it must have been a browser bug, because when I closed Chrome and re-opened it, this began working as expected. Is anyone aware of this kind of bug in Chrome?

Just an alternative : (without JS)
Use tabindex
Selector is :active:hover
.thing {
display: block;
width: 10em;
height: 10em;
background: green;
user-select: none;
outline: none;
}
.thing:active:hover {
cursor: move;
background: red;
}
<div class="thing" tabindex="1"></div>

drag != mousedown
Its a browser default dragging behaviour .Add the drag event with mousedown
$(document).ready(function() {
var $thing = $('.thing');
$thing.on('mousedown ,drag', function(e) {
$thing.addClass("moving");
console.log("mousedown");
}).on('mouseup', function(e) {
$thing.removeClass("moving");
console.log("mouseup");
});
});
.thing {
display: block;
width: 10em;
height: 10em;
background: green;
}
.moving {
cursor: move;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="thing"></div>

Related

JavaScript: mouse events stop firing while the mouse is down

I have encountered a situation where certain mouse events stops firing while the user is dragging with the mouse.
Here is a jsFiddle of the issue.
UPDATE: You can find here a much simpler jsFiddle which also displays the issue.
The context
I am creating a tool to crop images. To do this, I create a set of <div> elements over an image, and the user will be able to drag the corners and the edges of the parent <div> by dragging one of the child <div>s.
The details
On mousedown, I add two event listeners to the document body:
a mousemove event listener, to fire constantly
a mouseup event listener, to fire once and remove the mousemove listener when the user stops dragging the mouse
The result
This always works perfectly the first time you drag a corner or an edge. However, the second time you drag the same corner or edge, the mousemove event fires between 1 and 6 times (in my experience) and then stops. The mouseup event is not fired when you release the mouse, but from that moment on, the mousemove events resume, and a subsequent click-and-release will generate a mouseup event.
Investigation
In Chrome Dev Tools, I can see that the event listeners are present by entering getEventListeners(document.body) into the console, or by checking in the Event Listeners pane of the Elements tab, for the body element.
Any help in understanding why this is occurring, and in how to resolve this issue will be greatly appreciated.
HTML
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset="utf-8">
<title>Crop Image</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div id="crop">
<div class="topLeft"></div>
<div class="top"></div>
<div class="topRight"></div>
<div class="right"></div>
<div class="bottomRight"></div>
<div class="bottom"></div>
<div class="bottomLeft"></div>
<div class="left"></div>
</div>
<pre id="feedback"></pre>
<script src="js/crop.js"></script>
</body>
</html>
CSS
div#crop {
position: relative;
width: 320px;
height: 320px;
cursor: pointer;
}
div#crop div {
position: absolute;
top: 0;
left: 0;
height: 20px;
width: 20px;
border: 1px solid #000;
box-sizing: border-box;
}
div#crop .top,
div#crop .bottom {
left: 20px;
width: calc(100% - 40px);
}
div#crop .left,
div#crop .right {
top: 20px;
height: calc(100% - 40px);
}
div#crop .topRight,
div#crop .right,
div#crop .bottomRight {
left: auto;
right: 0;
}
div#crop .bottomLeft,
div#crop .bottom,
div#crop .bottomRight {
top: auto;
bottom: 0;
}
pre#feedback {
position: fixed;
top:40px;
left:40px
}
JavaScript
"use strict"
let cropType
, counter
let div = document.getElementById("crop")
let feedback = document.getElementById("feedback")
document.body.onmousemove = log
div.addEventListener("mousedown", startResizeImage, false)
function startResizeImage(event) {
log("startResize")
cropType = event.target.className
counter = 0
document.body.addEventListener("mousemove", resizeImage, false)
document.body.addEventListener("mouseup", stopResizeImage, {once: true})
}
function resizeImage(event) {
log ("drag (" + ++counter + ") " + cropType)
}
function stopResizeImage() {
log("stopResize")
document.body.removeEventListener("mousemove", resizeImage, false)
}
function log(data) {
data = data.type || data
let text = data+": "+event.clientX+", "+event.clientY
let lines = feedback.innerHTML.split("<br>")
if (data === "mousemove") {
text = text + "\n" + lines.pop()
} else {
text = lines.shift() + "\n" + text
}
feedback.innerText = text
}
The reason the second drag operation was failing was because the browser used the first drag-and-release to create a selection, whose anchorNode was the <div> that was clicked. The second drag then tried to move this selection. The clue that revealed this was that the cursor changed to a move closed hand, almost immediately after the second drag began.
The solution is to use CSS to prevent the draggable <div>s from being selected:
div#crop {
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* Likely future */
}
The irony is that, in the past when I have created a drag feature, I have always disabled selection, for cosmetic reasons. I had not realized that it was a key factor for the success of the operation.

Why does the mouseout handler behave so illogically in this case?

Red square is the part of a container with class "parent". If I hover mouse over that red square it disappears. But why? I expected that it shouldn't.
Expected behaviour: it does not disappear since red square is a part of ".parent" container and I have clearly stated, that the mouseout event occurs on that container.
There was a suggestion, that this question is a duplicate of
JavaScript mouseover/mouseout issue with child element
In some way - yes, but I think that this question provides value, because it not only provides the solution ("you can try this"), but also explains WHY you should use that and WHY the initial solution is not working as it is supposed to.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append("<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>");
$(this).on("mouseout", removeSquare);
}
$(".parent").on("mouseover", addSquare);
</script>
It's normal behaviour of .mouseout() event.
Show the number of times mouseout and mouseleave events are triggered.
mouseout fires when the pointer moves out of the child element as
well, while mouseleave fires only when the pointer moves out of the
bound element.
You should use .mouseenter() and .mouseleave() events,
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
$(".parent").on("mouseleave", removeSquare);
.parent {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="parent">Hover mouse over this text<br></span>
As other people have noted, your original problem is that mouseover and mouseout events also fire for child elements. The solution to that issue is either to use jQuery's mouseenter and mouseleave events, or simply to replace the JS code with the CSS :hover pseudo-class.
However, the reason why the other JS and CSS solutions posted here sometimes behave erratically (causing the square to disappear if you move the mouse over it slowly, but not if you move it fast, and not on all browsers even if you move it slowly) is because, depending on your browser and font settings, there may or may not be a small gap between the top line of text and the square below it. If the gap exists, and your mouse cursor hits it while moving from the text to the square, the browser will consider the mouse to have left the parent element, and will thus hide the square.
Setting a (light blue) background color on the parent element shows the issue clearly; depending on what font and line height the browser chooses, the parent element and the box can look like this:
or like this:
Manually setting a particularly large line height makes the problem easily reproducible (CSS example based on Thomas van Broekhoven's answer):
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
There are two general ways to fix this issue. The simplest option, where practical, is to make the parent element a block, thereby eliminating the gaps between the lines. You may also wish to add position: absolute to the square's style, so that it won't expand its parent element when it appears:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
background-color: red;
width: 50px; height: 50px;
}
.parent {
display: block;
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'></span></span>
Alternatively, if you really want to stick with an inline parent element (e.g. because you want it to be able to wrap across several lines of text), you can set a negative top margin on the square to make sure it overlaps the line of text above it. If you don't want the square to visibly overlap the text, you can further move all the visible content of the square into an inner element and set a corresponding positive top margin on it, like this:
.kvadrat {
display: none;
}
.parent:hover > .kvadrat {
display: inline-block;
position: absolute;
margin-top: -1em;
border: 1px dashed gray; /* to show the extent of this otherwise invisible element */
}
.kvadrat > .inner {
display: block;
margin-top: 1em;
background-color: red;
width: 50px; height: 50px;
}
.parent {
line-height: 2.0;
background: lightblue;
}
<span class="parent">Hover mouse over this text!<br>
Here's another line of text.<br>
<span class='kvadrat'><span class='inner'></span></span></span>
I know this is not directly answering your JavaScript question, but I would like to open your eyes if you're not bounded to JavaScript. You can easily achieve this with CSS.
.kvadrat {
display: none:
}
.parent:hover > .kvadrat {
display: inline-block;
background-color: red;
width: 50px;height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='kvadrat'></span></span>
You can achieve the same using CSS.
.child {
display: none:
}
.parent:hover > .child {
display: inline-block;
background-color: red;
width: 50px;
height: 50px;
}
<span class="parent">Hover mouse over this text<br>
<span class='child'></span>
</span>
It is because of event bubbling. When you enter the child span, you jQuery will fire mouseout because you've now gone to a child span. If you want to keep it going, use mouseenter and louseleave which does not fire until you leave the actual element, regardless of child elements.
<span class="parent">Hover mouse over this text<br></span>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script>
function removeSquare()
{
$(this).find(".kvadrat").remove();
}
function addSquare()
{
$(this).append ( "<span style='display:inline-block;width: 50px;height: 50px;background-color:red' class='kvadrat'></span>" );
$(this).on("mouseleave", removeSquare);
}
$ ( ".parent" ).on ( "mouseenter", addSquare );
</script>

Jquery click on popup div to show another div

The requirement is user can Click on black box to show orange box, and click on orange box to show red box, but the orange box and red box should be hidden
when user click anywhere of the document except the orange box or the
red box itself.
But currently the issue is that we cannot click on orange box to show red box
Would much appreciate if you could help me out, thanks a lot
Demo link: http://plnkr.co/edit/OqlfbmFPKdXx0wDhnLxZ?p=preview
$(function() {
$('#mypop').click(function(e) {
event.stopPropagation();
});
$(document).on('click', '#myclick', function() {
$('#mypop').toggle();
$(document).one('click', function() {
$('#mypop').hide();
});
});
$(document).on('click', '#myclick1', function() {
$('#mypop2').show();
});
$(document).on('click', '#myclick2', function() {
$('#mypop2').show();
});
})()
#mypop {
background-color: orange;
position: absolute;
top: 130px;
left: 50px;
width: 150px;
padding: 15px;
}
.mydiv {
background-color: black;
padding: 30px;
width: 50px;
cursor: pointer;
color: white;
}
#mypop2 {
margin-top: 150px;
background-color: red;
width: 100px;
height: 50px;
padding: 18px;
display: none;
}
#myclick1,
#myclick2 {
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myclick" class='mydiv black-box'>
click me!
</div>
<div id="mypop" style="display:none;" class='orange-box'>
<p>hello world</p>
<div id='myclick1'>BUTTON1</div>
<div id='myclick2'>BUTTON2</div>
</div>
<div id="mypop2" class='red-box'>
Hello World!!!
</div>
try this. I think this is what you are excepting but I'm not sure since you keep editing your question.
Demo Link: http://plnkr.co/edit/n7rdgqTwiFrXtpgoX4TQ?p=preview
$('#myclick1').click(function(){
$('#mypop2').show();
});
$('#myclick2').click(function(){
$('#mypop2').show();
});
You have couple of things mixed up.
The main stop-point was the very first event listener
$('#mypop').click(function(e) {
which is incompatible with the rest of listeners
$(document).on('click','#myclick1',function(e){
after I have changed it to
$(document).on('click','#mypop', function(e){
the other listeners have started working.
Second thing is that for embedded elements (parent-child) you need to stop event propagation, otherwise the parent event is triggered as well (which is not desired)
$(document).on('click','#myclick1',function(e){
e.stopPropagation();
:
});
I have also changed the CSS a bit and added class hide to use instead of styles. Toggling this class is what hides and shows an element.

How to enable javascript mouse events on overlapping html elements?

This probably cannot be done, but I have a fixed-position div on top of inline html in the page body. The inline html has clickable elements, and the fixed div has a hover event.
The fixed element is an empty div, so it is invisible.
Currently, the fixed element is blocking click events on the item under it.
Is it possible?
This solution is too complicated
https://stackoverflow.com/a/9616491/209942
Possible solution?
https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
Thx
The fixed element should not be prevent the clicks from the item under it unless you are stopping the event propagation.
See this fiddle: https://jsfiddle.net/pv0mygz5/
-- it demonstrates that without event.stopPropagation the event should be intercepted by the listener on the span element.
$('#click-me').on('click', function (e) {
console.log('click triggered');
});
$('.box').on('mouseover', function (e) {
//don't stop event from bubbling
console.log('hover triggered');
});
Could you also include a code snippet that demonstrates your problem?
although IE10 doesn't support it you can use
pointer-events: none;
http://jsfiddle.net/leaverou/XxkSC/light/
In this fiddle you can see a drop down being covered with other elements, the other elements has pointer-events: none so you can click on the arrow down button and the click actually goes to the select element itself.
BR,
Saar
You can also try using z-index. Depending on your layout it may not be a solution, but if your front div is invisible, then it shouldn't create unwanted effect. Like this for example:
document.querySelector('#under').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
document.querySelector('#notunder').addEventListener('click', function(e) {
e.target.style.color = "blue";
});
#fix {
width: 60px;
height: 200px;
position: fixed;
z-index: -1;
top: 0px;
border: 1px solid black;
}
#under {
display: inline;
}
#fixnozindex {
width: 100px;
height: 200px;
position: fixed;
left: 75px;
top: 0px;
border: 1px solid black;
}
#notunder {
display: inline;
}
<div id="fix"></div>
<div id="under">Clickable</div>
<div id="fixnozindex"></div>
<div id="notunder">Not clickable</div>

How to make toggle off when on mouse over is done?

I have created an element that is displayed when I am over a particular box.
If I move my mouse over the box I can see my element but then I need to move my mouse in and out twice for the element to disappear. How can I fix it? Shouldn't the element hide itself once I move the mouse out?
How do I make my box only show when mouse is over the box?
<script>
$("#box").on("mouseover",function()
{
$("#my-box").toggle();
});
</script>
I tried to hide it myself, but it didn't work:
$("#box").on("onmouseout", function()
{
$("#my-box").hide();
});
You can use mouseover and mouseout in a same eventlistener like one below:
$("#box").on("mouseover mouseout",function()
{
$("#my-box").toggle();
});
#my-box{
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box">
Box here
</div>
<div id="my-box">
My Box
</div>
FIDDLE DEMO
The problem with your code is you're using onmouseout instead of use mouseenter and mouseleave method.
You can use hover:
$('#box').hover(function(){
$('#my-box').toggle();
});
You can use combination of both
$("#box").mouseover(function() {
$("#my-box").show();
}).mouseout(function() {
$("#my-box").hide();
});
Example
jQuery Solution
HTML
<div class="box" id="action"></div>
<div class="box" id="hidden"></div>
JS
$("#action").on("mouseover mouseout",function()
{
$("#hidden").toggle();
});
CSS
.box{
width: 30px;
height: 30px;
background: red;
margin: 10px;
}
#hidden{
display: none;
}
JSFiddle
Allthough it would be better doing this by just using CSS.
CSS Only Solution
.box{
width: 30px;
height: 30px;
background: red;
margin: 10px;
}
#action:hover + #hidden{
display: block;
}
#hidden{
display: none;
}
JSFiddle

Categories

Resources