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.
Related
Introduction
I'm using Semantic-UI's sidebar functionality, which gives you a button that triggers a sidebar that pushes the content from the left (in this case).
I want to unfold that same sidebar by hovering with the mouse on the left side. I realize there are several ways to do it (as these often do. Maybe just checking the X position of the mouse would work but that's beside the point); I chose to create a transparent div on the left side and make its :hover pseudo-class to trigger the sidebar:
// create sidebar and attach to menu open
$('.ui.sidebar').sidebar('attach events', '.toc.item');
// hover transparent div to trigger the sidebar too:
$('.sidebar-trigger').hover(function() {
$('.ui.sidebar').sidebar('show')
});
// hide() and show() the sidebar accordingly to use the sidebar:
$('.ui.sidebar').sidebar('setting', {
onShow: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
Problem
Now, it all works except for one occasion: when you don't stop moving the mouse as the sidebar opens. I've looked at $(document).on('transitionend', function(event) { ... } and that mouse effectively prevents the transition to finish.
Resources
I've put a blue background on my .sidebar-trigger and made a small video/gif so as to be clearer.
I moved the mouse like a crazy creature but with natural gestures the problem occurs as well.
I'm using Semantic-UI's guide on this thing: http://semantic-ui.com/modules/sidebar.html#/settings (I've also tried onVisible and onHide with no luck)
This is a OSX Yosemite 10.10.3 running Chrome 45.0.2454.101 (64-bit)
jsfiddle with the problem at hand
PS: It seems it might be an OSX Chrome bug?
I would try using one and mouseover:
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
Then, when it has finished animating, reattach the event:
$(document).on('transitionend', function(event) {
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
});
I think what is happening is that the hover event is getting called multiple times - every time the element is hovered, then goes over a child element, and then goes back over the hover element, and things are getting mixed up at some point. So you need to only call show if it's not already shown.
Here is a working example: Fiddle
I believe when the element was hovered, it was adding a classes 'uncover' and 'visible', and another called 'animating' which wouldn't fire until the mouse stopped moving. I changed the jQuery slightly to only add classes 'uncover' and 'visible', and it still animated okay. However, the body was pushing right too far by 175px, so I had to edit the class that was causing that (noted below) from 260px to 85px. This DOES get the menu acting properly though from my understanding.
$('.sidebar-trigger').mouseenter(function() {
$('.ui.sidebar').addClass('uncover, visible');
$('body').addClass('mleft175');
});
$('body').click(function() {
$('.ui.sidebar').removeClass('uncover, visible');
$('body').removeClass('mleft175');
});
and then add overriding class
.ui.visible.left.sidebar ~ .pusher
{
-webkit-transform: translate3d(85px, 0, 0);
transform: translate3d(85px, 0, 0);
}
Right now it is set to hide the menu when the body is clicked. Alternatively you can hide it when the mouse leaves the sidebar menu:
$('.ui.sidebar').mouseleave(function(){
$(this).removeClass('uncover, visible')
});
Ok, my first answer was (of course) way too much work for what it really needed. The onVisible seems to work perfectly. Was that not working for you? Demo HERE
Simply change 'onShow' to 'onVisible' in your sidebar setting:
$('.ui.sidebar').sidebar('setting', {
onVisible: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
As shown on the Semantic UI site, the onVisible fires when the animating starts. The onShow fires when the animating finishes. So what you were doing was hiding that blue / transparent bar when the animation was finally done (the .animating class noted in my previous answer), as opposed to when it starts. If you need further explanation please let me know.
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.
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/
I have an iframe #viewer and an absolutely positioned element #appear_above_viewer that appears above it. I want #appear_above_viewer to fade in when the mouse goes over #viewer, and fade out when the mouse leaves.
So far I have this code:
$("#viewer").hover(
function(){ $("#appear_above_viewer").animate({ opacity: 1 }, 'slow'); },
function(){ $("#appear_above_viewer").animate({ opacity: 0 }, 'slow'); }
);
It seems to work, until you hover your mouse over #appear_above_viewer, and it conveniently disappears. I presume this is because an absolutely positioned element is considered something different to the iframe, and it's position on the screen makes no difference.
I want #appear_above_viewer to only disappear when the mouse leaves #viewer, and anything above it entirely. Is this possible?
(I've read other questions about this but none of them seem to work for my situation)
Still not positive I know exactly what behavior is expected, but you likely just need to apply the pointer-events attribute in css set to none on your appear_above_viewer.
Here is a jsFiddle demonstrating something similar to what I think you want.
EDIT
You can use this question for a cross-browser solution:
Click through a DIV to underlying elements
You can try this.
onmouseout =function(e){
if ( e && e.preventDefault )
e.preventDefault();
e.stopPropagation();
else
window.event.cancelBubble = true;
window.event.returnValue = false;
return false;
}
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).