Fast moving mouse never triggers mouseleave event - javascript

When mousing over certain buttons on my site, I'd like for tooltips to appear that instruct users. Basically, whenever a button with the 'has_tooltip' class is moused over, a tooltip is attached.
$('.has_tooltip').live({
mouseenter : function(e) {
if($('#tooltip_container').length > 0){
$('#tooltip_container').remove();
}
var $t = $(this), text = $t.attr('rel'), left = e.pageX-25, top = e.pageY-25;
if($t.attr('rev') === '1') {
text += ' <span class="tooltip_warning">You must be logged in to make use of this.</span>'
}
$tooltip = $('<div id="tooltip_container">'+text+'</div>');
$('body').prepend($tooltip);
$tooltip.css({
left: left+'px',
top: top+'px'
});
},
});
And when a user's cursor leaves the newly created tooltip box, it should disappear
$('#tooltip_container').live({
mouseleave : function(e){
$(this).remove();
}
});
However, a fast moving mouse over a button with the 'has_tooltip' class adds the tooltip, but moves too quickly for the mouseleave event to trigger.
Anyone have some tips on how I can fix this?

'If the mouse does not enter the tooltip (the tooltip appears below the mouse), the browser may not trigger the mouseleave event. You may want to add an additional selector. Try this:
$('#tooltip_container','.has_tooltip').live({
mouseleave : function(e){
$('#tooltip_container').remove();
}
});
I would highly recommend removing the HTML from your tooltip method though... try creating an empty div and add the tooltip text and positioning when you go to show it -- try to add as little to the DOM as possible (create a hidden div for most of the tooltip HTML and only change the actual text content of it as necessary).
Ideally, your mouseenter should simply replace the tooltip text and show the div with correct positioning. The mouseleave event should just hide() the tooltip div (not remove it from the DOM just to be created and added again later).

Related

jQuery flot the tooltip will not hide when I move the mouse quickly out of plot

Please have a look at the example of jQuery flot with tooltip, which is at http://www.flotcharts.org/flot/examples/interacting/
When I put the mouse and move the normally the tooltip appears and disappears normally.
However when I move the mouse quickly out of plot, the tooltip will remain.
Please see below animated gif which shows slow and quick mouse movement.
The main part code which handles the hover is as below:
$("#placeholder").bind("plothover", function (event, pos, item) {
if (item) {
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
$("#tooltip").html(item.series.label + " of " + x + " = " + y)
.css({top: item.pageY+5, left: item.pageX+5})
.fadeIn(200);
} else {
$("#tooltip").hide();
}
});
I think it is because when I move the mouse quickly the $("#tooltip").hide(); executes when the $("#tooltip").html is not rendered.
#placeholder, which is the graph container, is binded to the plotover event.
So when such event occur on this container element, the script checks if there is an item returned in order to decide to show or hide the tooltip.
Now when your mouse is outside this container, there is no more plothover event triggered. So the script is not executed to hide the tooltip.
To fix that, I would add a script which would hide the tooltip when the mouse leaves the container like this:
$("#placeholder").on("mouseleave", function(){
$("#tooltip").hide();
});
Also, notice that .bind() is deprecated. It is preferable to use .on().
EDIT
I didn't think about delays in my first answer.
When passing your mouse fast on the chart item... There is something like a 600ms delay for the tooltip show animation.
So the mouseleve event is trigered too early to close it.
Try this now:
$(document).on("mousemove",function(e){
if ( $(e.target).closest("#placeholder").length == 0 ){
$("#tooltip").hide();
}
});
So now, on mousemove, this script will check if the mouse is over the chart container on any of his childs. If not, it will hide the tooltip.
I think the explanation in the other answer is not complete:
The problem occurs because the hide() executes before the fadeIn() is finished. And at the end of the fadeIn() a show() is executed implicitly.
Using fadeOut(1) instead of hide() fixes this because the fadeOut() is queued after the fadeIn() instead of executed directly like the hide().

Let draggable do its work only if mouse cursor on a draggable element is default cursor

I have used jQueryUI's draggable method on a div.
var data = '<div> <p> Draggable Div </p> </div>'
$(data).appendTo($(this)).draggable();
It works fine.
My application heavily depends on mouse cursor type.
So, I change cursor every now and then programatically.
Now, I want that:
Draggable class should only be doing its work if the mouse cursor of a draggable element is default cursor.
How can I change this behavior without making changes in jQueryUI.js file if possible.
You can make use of the start event.
$("#data").draggable({
start: function(event, ui) {
if ($(this).css('cursor') === 'auto') { //replace "auto"
event.preventDefault();
}
}
});
I have created a working fiddle here.
I hope this helps.

Drag Leave and Drag Enter

When I drag a file over to my window I wish to show an overlay, and when the file is dragged off the window I wish to remove the overlay.
$(window).on('dragleave', this.onDragLeave);
$(window).on('dragenter', this.onDragEnter);
p.onDragEnter = function(e) {
console.log('ENTER');
};
p.onDragLeave = function(e) {
console.log('LEAVE');
};
The above works fine, when I enter and leave the window it logs correctly.
The problem starts when I start fading in and out my overlay:
p.onDragEnter = function(e) {
console.log('ENTER');
$('#drag-overlay').fadeIn();
};
p.onDragLeave = function(e) {
console.log('LEAVE');
$('#drag-overlay').fadeOut();
};
With the above, it just fades in and out again and again. I'm not sure whats going on, it's as if when the overlay fades in it fires a drag leave, i'm not sure why?
The overlay is just an absolute div, width and height 100%.
The problem is that by showing an overlay, you are causing the dragged item to leave the parent and drag into the overlay. Then hiding the overlay causes drag to trigger in the parent.
Fortunately, the solution is simple and can be done in css:
#drag-overlay
{
pointer-events: none;
...
}
See this jsfiddle for a working solution.
If you remove pointer-events:none you get the same behaviour. pointer-events:none just means that the parent ondragleave method isn't fired when dragging over the overlay.

Conflicting click and hover events on the same element

I have a refresh "button" (actually a png image) which the user can hover their mouse over, turning it from gray to blue. When refresh is clicked, the image changes to a play "button" which exhibits similar color-changing behavior, except when you click on the play image it should switch back to the original refresh image.
The problem is that, after clicking on the refresh image, when I click on the play image without removing my mouse from the image, it doesn't change back to the refresh image.
I have already looked into event propagation stopping.
Here is my code:
$('#refresh').click(function (event) {
if (!tMinusZero) {
$('#refresh').html("<img src='play_small_hover.png'>");
tMinusZero = true;
event.stopImmediatePropagation();
} else {
$('#refresh').html("<img src='refresh_small_hover.png'>");
tMinusZero = false;(
event.stopImmediatePropagation();
}
});
$('#refresh').hover(function () {
if (!tMinusZero) {
$('#refresh').html("<img src='refresh_small_hover.png'>");
} else {
$('#refresh').html("<img src='play_small_hover.png'>");
}
}, function () {
if (!tMinusZero) {
$('#refresh').html("<img src='refresh_small.png'>");
} else {
$('#refresh').html("<img src='play_small.png'>");
}
});
Some interesting things I have noticed whilst trying to debug:
If I move my mouse through #refresh too fast the hover will 'stick' i.e. the implicit mouseleave won't fire.
Commenting out the hover code fixes the clicking problem.
Leaving the hover code in, if I click outside of the image but inside of the div, without removing my mouse from the div, fixes the clicking problem.
Removing my mouse from the div before trying to click again fixes the clicking problem.
After clicking on the image, the image it changes to will flicker between the hover and non-hover image if I move my mouse over it, but not if I first remove my mouse from the div.
When I experience the clicking problem, the offending image will flicker momentarily, as if switching to one of the non-hover images, and then quickly changing back to the offending image.
It seems to me that my two event handlers have conflicting interests, but stopping the event propagation doesn't seem to help.
Why don't you try to tackle your problem with CSS, i think it will be more elegant, make a small DIV, with a background corresponding to your image, define a hover state and an active state, plus a small script to change between 2 more additional states
Something like:
CSS:
#refresh[data-state=notclicked]
{
background-image:url('play_small.png');
cursor:pointer;
}
#refresh[data-state=notclicked]:hover
{
background-image:url('play_small_hover.png');
cursor:pointer;
}
#refresh[data-state=clicked]
{
background-image:url('refresh_small.png');
cursor:pointer;
}
#refresh[data-state=clicked]:hover
{
background-image:url('refresh_small_hover.png');
cursor:pointer;
}
Of course you will have to define the width and the height in the CSS, to a fixed width.height which matches your png size.
Than the js:
$("#refresh").click(function(){
if ($(this).attr('data-state')=='clicked') {$(this).attr('data-state','notclicked');}
else {$(this).attr('data-state','clicked');}
});
If I understand correctly how you want it to behave, your code seems to work just fine, even though there's a typo in the click event (round bracket).
tMinusZero = false;(
Also, just for improve the code you can replace
$('#refresh')
with
$(this)
inside the event as it'll refer to the dom element you attached the event to.

Dragging a div out from underneath multiple other divs

I have been trying to create a test case for a webapplication we are making and I am quite close to a solution, but the final part has me stumped...
We need to be able to drag divs around (no problem there), but some of the divs need to be locked in place (again, no problem).
The problem arises when a draggable div is stuck underneath a non draggable div. I have fixed this somewhat, but it only works if there is only ONE non draggable div on top of the draggable one.The moment it overlaps with another non draggable div, it won't work. Which is weird, as I am correctly accessing the draggable div.
my HTML:
<body>
<div id="div1" class="draggable"></div>
<div id="div2" class="locked"></div>
<div id="div3" class="locked"></div>
</body>
And here is my javascript:
<script>
$(document).ready(function () {
$(document).mousemove(function (e) {
window.mouseXPos = e.pageX;
window.mouseYPos = e.pageY;
});
});
$(function () {
$(".draggable").draggable();
});
$('.locked').ready(function () {
$('.locked').mousedown(function (e) {
var layer = e.target;
$("#data").html($("#data").html() + " " + layer.id);
$(layer).addClass("hide");
var lowerLayer = document.elementFromPoint(window.mouseXPos, window.mouseYPos);
if ($(lowerLayer).hasClass("locked")) {
$(lowerLayer).mousedown();
}
else if ($(lowerLayer).hasClass("draggable")) {
$("#data").html($("#data").html() + " " + lowerLayer.id);
$(lowerLayer).trigger(e);
}
$(layer).removeClass("hide");
});
});
</script>
Okay, so the idea is this, I use jquery to set everything with the class "draggable" to be able to be dragged. The body gets a mousemove event to store the current mouse position. While all the elements with the "locked" class, will fire a mousedown event. In this event, I hide the element I am clicking on by adding a class called "hide" (which contains only a display: none).
At this point, the element underneath the clicked element is visible. Using the elementFromPoint combined with my stored mouse positions, I grab the lower element.
By then checking if it is "locked" or "draggable" I can determine what this element should do. If it's locked, I force it to execute a mousedown event. If it's draggable, I use the .trigger(e) to start dragging.
I then remove the "hide" class again, so that the element does not stay hidden.
Using the div called data, I can see that the function does indeed reach the draggable div. However, if both locked divs are on top of it, it will not start dragging. If there is only one locked div on top, I can then start dragging the draggable div.
In my opinion, this should work without any problems. I mean, it works with only 1 overlapping div, and even with 2 (or more), I am still triggering the code in the else if statement.
What am I missing/overlooking?
-Ferdy
I rewrote your script slightly differently and it seems to work. The problem might be that you're using e.target, but i'm not really sure. Here's what i did:
$(".draggable").draggable();
$(".locked").mousedown(function(e) {
var layer = $(this);
layer.addClass("hide");
var lowerLayer = $(document.elementFromPoint(e.pageX, e.pageY));
if (lowerLayer.hasClass("draggable") || lowerLayer.hasClass("locked"))
lowerLayer.trigger(e);
layer.removeClass("hide");
});
http://jsfiddle.net/AXycq/

Categories