Stop duplicate mouse over detection - javascript

I have a popup that is executed on mouseover with jquery.
Within that function I have a second delay before the popup displays using settimeout
Problem is if in that second they mouse over multiple times then multiple popups are triggered.
$('#div').mouseover(function() {setTimeout("popup()",1000);});
What I need to do is disable the detection and then re enable it in popup().
How might I do that?

You can use .hover() with a clearTimeout(), like this:
$('#div').hover(function() {
$.data(this, 'timer', setTimeout(popup, 1000));
}, function() {
clearTimeout($.data(this, 'timer'));
});
This clears the timeout you're setting if the mouse leaves, you'll have to stay on the element for a full second for the popup to trigger. We're just using $.data() on the element to store the timer ID (so we know what to clear). The other change is to not pass a string to setTimeout() but rather a reference directly to the function.

I guess something like this
(function(){
var popup_timer = 0;
$('#div').mouseover(function() {
clearTimeout(popup_timer);
popup_timer = setTimeout("popup()",1000);
});
});
EDIT updated code, clearTimeout added, wrapped

Related

jQuery trigger event with delay

I have a situation in which I have a series of buttons on a page. Clicking a button triggers several functions and runs a series of complex calculations.
Then I have what is essentially a "click all" button that will trigger a click event on each button:
$('.myBtn').trigger('click'); // $('.myBtn') returns a list of all buttons
This works fine in most modern browsers, but in IE the trigger('click') takes a long time to run and I often get a 'script is taking too long' error.
Unfortunately, the way things are set up there's no real way to avoid the heavy calculations on click.
So I'm thinking of adding some sort of delay. So on "click all", trigger btn1 click, wait 200ms, trigger btn2 click, wait... etc.
I've tried things like:
$('.btnAll').each(function() {
var el = $(this);
setTimeout(function () {
el.trigger('click');
}, 200);
});
But I don't think this works correctly because of the way .each() calls are queued or something(?). Event queueing and synchronous calls are still a little unclear to me.
Any thoughts on how I can make this work?
.each() calls are not 'queued', they just execute the given function on each element of the collection, one after the other. So you set for each button-click a timeout of 200ms. The result: 200ms later all buttons are triggered at (nearly) same time. If you want to stagger the clicks with delay in between, you must give them different times like so:
$('.btnAll').each(function(i) { // i is index of this in the collection
var el = $(this);
setTimeout(function () {
el.trigger('click');
}, i * 200); // each button gets a multiple of 200ms, depending on its index
});
This triggers the first button immediately, the second 200 ms later, the third.... .
Try it
function myEach(){
$('.btnAll').each(function() {
$(this).trigger('click');
});
}
setTimeout(myEach(),200);

Trying to do things the proper Ember.js way... Why won't this work?

I have a little box on my page where users can drag files and drop them. It gets a shadow when something is dragged into it and the shadow changes color when it's dropped. The shadow then disappears after a little time passes.
This is what I think my event's code in the view should look like:
App.AddView = Ember.View.extend({
classNameBindings: ['enter:enter:leave', 'dropped:dropped:leave'],
enter: false,
dropped: false,
text: 'Drag image files here.',
drop: function(event) {
this.set('text', 'Thanks!');
this.set('enter', false);
this.set('dropped', true);
setTimeout(function() {
this.set('text', 'Drag image files here.');
this.set('dropped', false);
}, 3500);
event.preventDefault();
event.stopPropagation();
},
[...]
However, I am having a couple of problems with this:
this.set('text', [sometext]) doesn't seem to be doing anything, either inside the timeout function or outside of it. However, $('#drop).text([sometext]) works both inside the timeout function and outside of it. I don't understand why this is.
this.set('dropped', false) (inside the timeout function) doesn't seem to be removing the "dropped" class from the element, even though this.set('dropped', true) (outside the timeout function) does work. Again, I don't understand what's going on here.
The adding of the "dropped" class only seems to happen once, even if I wait for it to be removed by the timer and then drag another item into the box. The dragEnter event that adds the "enter" class (and the dragLeave event that removes it ... neither one included in the code above for brevity) seems to be working fine, both before and after an item has been dropped.
What is the proper Ember.js way to change the text and add/remove classes on a view with a timer?
this
is wrong when you use it in the setTimeOut function because it is bound to the incorrect object. To fix this, you could say
setTimeout(function () { blah }.bind(this), interval)
or do it the usual way everyone does by assigning
var self = this;
setTimeOut(function() { //use self here }, interval )
But, the use of setTimeOut is frowned upon in the world of ember. A better solution would be to use the
Ember.run.later
which is provided by ember to basically mimic setTimeOut.

Is there anyway on onmouseover to fire after mouseovering after a few seconds?

I want to make sure the user wants to have something pop up by mouseing over a div. I don't want the user to accidentally trigger something to pop up without intent. setTimeout doesn't work because even if it's very brief, the pop up will still pop up, it's just delayed. What I want is the user to mouseover something for 1sec for a pop up to display.
**update:
When I do this:
<div onmouseover="myTimer=setTimeout('display(this)', 5000);">
the timer works and it is displayed after 5 seconds but this is not passed and I can't get the element via $(element).next(), etc.
When I do this:
<div onmouseover="myTimer=setTimeout(display(this), 5000);">
the timer doesn't work. What is wrong, how can I get the timer and the this to be passed?
Thanks!
**update2: the this problem from here states: "Code executed by setTimeout() is run in a separate execution context to the function from which it was called. As a consequence, the this keyword for the called function will be set to the window (or global) object, it will not be the same as the this value for the function that called setTimeout."
I found the answer to overcome this here where you have to "save a reference to the context where the setTimeout function call is made"
This should work for you:
<div id="example">Hover me</div>
<script type="text/javascript">
(function(){
var timer;
var el = document.getElementById('example');
el.onmouseover = function(){
timer = setTimeout(function(){
display(el);
// If display takes no arguments and uses the "this" keyword inside it
// Use display.call(el); instead
}, 1000);
}
el.onmouseout = function(){
clearTimeout(timer);
}
})();
</script>
Example on JSFiddle
I'm pretty sure that this should works:
onmouseover="myTimer=setTimeout(yourFunctionName, 1000);"
You have also to add:
onmouseout="clearTimeout(myTimer);"
On mouse over you can start a timer. On mouse out reset the timer to 0. If the timer reaches 1s, you can trigger your event and reset the timer to 0.
I also later came across something called the hoverIntent jQuery Plug-in, worth checking out.

Check if the jQuery page load events fired already

Is there a way to check if jQuery fired the page load events yet, or do you have to roll your own? I need to alter the behavior of links, but I don't want to wait until the page finishes loading because the user could conceivably click on a link on, say, the top half of the page before the page finishes loading. Right now I'm doing it like this:
var pageLoaded = false;
$(function() {
pageLoaded = true;
});
function changeLinks() {
$("a[data-set-already!='true']").each(function() {
$(this).attr("data-set-already", "true").click(...);
});
// Is there something along the lines of jQuery.pageWasLoaded that I can
// use instead?
if (!pageLoaded) {
window.setTimeout(changeLinks, 100);
}
}
changeLinks(); // Added per #jondavidjohn's question
Since you are using the document ready shorthand, I'm guessing you mean when the dom is loaded. For this:
$.isReady
You could use setInterval and clear the interval on domready:
var changeLinksInterval = setInterval(function () {
$("a[data-set-already!='true']").each(function() {
$(this).attr("data-set-already", "true").click(...);
});
}, 100);
$(function () {
clearInterval(changeLinksInterval);
});
By the way, in your code example, you shouldn't need .each() - you should be able to call .attr() and .click() directly and let jQuery do the looping. Unless there is more to your .each() code that you didn't post.
$("a[data-set-already!='true']").attr("data-set-already", "true").click(...);
you could use .live() to initiate a click event that needs additional work when binding.
$("a[data-set-already!='true']").live(function(){
// since this event will only fire once per anchor tag, you
// can safely bind click events within it without worrying
// about getting duplicate bound click events.
var $this = $(this);
$this
.data("dataSetAlready",true)
.click(myClickHandler);
});
this is also a useful technique for late-initializing plugins on elements that may not exist at domReady.

check for ('div')mouseenter on ('a')mouseleave

my problem is following:
I got a trigger(a) and a popup(div). The div doesn't lie nested inside the anchor.
When I hover over a, I want the div to show up.
When I go from a to the div, I want it to stay visible.
When I leave the div, I want it to close.
When I hover over a and leave without entering the div, I want the div to close.
I got most of that figured out, but now I'm struggeling with requierement no. 2.
When checking for mouseleave on a, I check if there is a mouseenter on the div. If it is, I want to abort the mouseleave. If not, I want to close the div.
What am I doing wrong? Is this even the right way to do this?
Here's the markup:
<a href="#" class="popup_toggle" style='display:block;width:50px;height:50px;border:1px solid red;position:relative;'>Toggle</a>
<div class="popup_div" style='position:absolute;top:50px;left:0px;border:1px solid blue;display:none;'>Popup</div>
Here's the jQuery:
$('.popup_toggle').mouseenter(function() {
var element = $(this).next('.popup_div');
$.data(this, 'timer', setTimeout(function() {
element.show(100);
}, 500));
});
$('.popup_toggle').mouseleave(function() {
clearTimeout($.data(this, 'timer'));
if($('.popup_div').mouseenter==true)
{
return false;
}
else
{
$('.popup_div').hide(100)
};
});
What you're trying to do is fairly simple. When entering the trigger, identify the panel (layer, popup, whatever), save reference to each other using .data() and have the event handlers check if the related targets are either the trigger (from the panel view) or the panel (from the trigger view). I threw something together. Have a look at the console log to see how this works… http://jsfiddle.net/rodneyrehm/X5uRD/
That will most likely not work...no. I would suggest that you add a mouseenter and mouseleave callback to you <div> element as well and have them set a global variable that tells your other callbacks how to handle their events, i.e. "if global variable is true, don't hide the popup on mouseleave, otherwise hide popup" or something like this.
The other approach would be to check whether the mouse is inside the popup when the mouseleave callback tries to hide the popup. That might be much more work than it is worth though.
I believe the problem with your implementation is that the mouseenter on the div will fire shortly after the mouseleave from the a.
This would give you something like:
$('.popup_toggle').mouseenter(function() {
// Clear any pending "hide" timer
// Set a show timer
});
$('.popup_toggle').mouseleave(function() {
// Clear any pending "show" timer
// Set a hide timer
});
$('.popup_div').mouseenter(function() {
// Clear any pending "hide" timer
});
Note that you'll have to make sure that you access the same timer from both the .popup_toggle event and the .popup_div event. You may want to consider using Ben Alman's doTimeout plugin to help with this. It (usually) results in much clearer code than manually working with setTimeout/clearTimeout.

Categories

Resources