Clearing timeouts with javascript - javascript

i'm having some sort of variable out of scope issue or something. in the function below, i'm creating or clearing a timeout based on whether the mouse is entering or exiting. it seems though, that even once the timeout has been created it's returning undefined on re-entry. not sure what i'm doing wrong here, thanks for your help!
jsFiddle example
JavaScript: (particular issue is within else conditional on line 35
var navLinks = $('nav li.sub');
navLinks.mouseenter(function(){
console.log('hovering on link');
var thiis = $(this),
subList = thiis.find('ul'),
autoClose;
if (!thiis.hasClass('out')){
console.log('isnt out');
/* Link */
thiis
/* Show submenu when entering link */
.addClass('out')
/* Hide submenu when exiting link */
.mouseleave(function(){
autoClose = setTimeout(function(){
thiis.removeClass('out');
}, 1000);
console.log('exiting link: timeout active', autoClose);
});
} else {
console.log ('is out', autoClose);
if (autoClose){
console.log('is out: clear timeout');
clearTimeout(autoClose);
}
}
});

Techno,
The simple answer is just to move var autoClose to an outer scope, but I think you can (and should) do more.
More specifically,
I don't think you want to attach the mouseleave handler inside the mouseenter handler. It can be permanently attached from the outset.
In the mouseenter handler, clearTimeout(autoClose) and thiis.addClass('out') can be executed unconditionally. There's no real economy in testing .hasclass('out').
Try this :
var navLinks = $('nav li.sub');
var autoClose;
navLinks.hover(function(){
var thiis = $(this);
clearTimeout(autoClose);
thiis.addClass('out');
}, function(){
var thiis = $(this);
autoClose = setTimeout(function(){
thiis.removeClass('out');
}, 1000);
});

As pointed in comments to question you have new timeout each time mouse hovers an item. Lets make new timeout variable for every item:
$('nav li:has(ul)').each(function(){
var par = $(this),
sub = $("> ul", this),
closeTO;
par.hover(
function(){
clearTimeout(closeTO);
par.addClass("out");
},
function(){
closeTO = setTimeout(function(){
par.removeClass("out");
}, 1000);
}
);
});
http://jsfiddle.net/ByuG3/1/

You should use a global object to store some vars like reference of timeout and interval.
In example you could declare such an object:
// Declare the context object in the global scope
var myContext = {
"myTimeout" : false
}
And then use the context object in your mouseenter and mouseleave handler functions.

Related

Using 'this' in nested functions

Wow.. to get real information about 'this' is not easy as google basically ignores the word.
The code opens an image from a database using the information from thumbnail.. the onlick works, and the hover code works, but I can't figure out how to get 'this' from the mouseenter to be used in the showModal function.
function showModal() {
$("body").css("overflow-y", "hidden");
$(".small").removeClass("smallHover");
$(".modal").fadeIn(200);
var altLong = $(this).attr("alt");
var altSplit = altLong.split("#");
$(".picTitle").text(altSplit[0]);
var srclong = $(this).attr("src");
var srcshort = srclong.split("_");
var srcextension = srclong.split(".");
$(".big").attr("src", srcshort[0]+'.'+srcextension[1]);
}
$(".small").click(showModal);
var timer;
$(".small").mouseenter(function() {
timer = setTimeout(function(){
$(this).showModal(); // **<--this is the line that doesnt work**
}, 2000);
}).mouseleave(function() {
clearTimeout(timer);
});
also if you could explain why you would use $(this) as a jquery object instead of just 'this' and how they differ, that would be great. Thanks in advance~!
There are two separate aspects to this.
Getting the right this in the setTimeout callback
Calling showModal with that this
#1 is addressed by this question's answers. You have several options, the simplest in this case (for now) probably being to use a variable:
$(".small").mouseenter(function() {
var _this = this; // ***
timer = setTimeout(function(){
$(_this).showModal(); // ***
}, 2000);
}).mouseleave(function() {
clearTimeout(timer);
});
...but that code still won't work, because showModal isn't a property of jQuery objects, it's a standalone function. To call it with a specific this, you'd use Function.prototype.call:
$(".small").mouseenter(function() {
var _this = this;
timer = setTimeout(function(){
showModal.call(_this); // ***
}, 2000);
}).mouseleave(function() {
clearTimeout(timer);
});
(Alternately, change showModal to accept the element as a parameter and then just pass it as an argument.)
More on this in this question's answers as well, as well as this (old) post on my anemic little blog.
this will also work if you could change your showModel function like this :
$.fn.showModal = function() {
$("body").css("overflow-y", "hidden");
$(".small").removeClass("smallHover");
$(".modal").fadeIn(200);
...
}
and inside timer method
$(this).showModal();

JS from HOVER to ONLOAD

i'm trying to edit some existing code to fulfill my need.
https://tympanus.net/codrops/2010/02/08/awesome-css3-jquery-slide-out-button/
It's effect of sliding out content after you hover over it with the mouse is what i am after. Seems simple enough, but i would rather have it on a (automatic) timer. Or rather have no interaction from the user at all for it to work.
Let's say it starts closed, then opens up after 2 seconds. Stays open voor 5 seconds and then closes again. All without using the mouse to activate it.
<script type="text/javascript">
$(function() {
$('.slidebttn').hover(
function open() {
var $this = $(this);
var $slidelem = $this.prev();
$slidelem.stop().animate({'width':'225px'},800);
$slidelem.find('span').stop(true,true).fadeIn();
$this.addClass('button_c');
},
function close() {
var $this = $(this);
var $slidelem = $this.prev();
$slidelem.stop().animate({'width':'70px'},700);
$slidelem.find('span').stop(true,true).fadeOut();
$this.removeClass('button_c');
}
);
});
</script>
Any tips on what i need to edit to reach my goal?
JSFiddle: https://jsfiddle.net/mj7yumfw/14/
Try this one:
<script type="text/javascript">
$(function() {
setTimout(initSlider, 2000);
});
function initSlider() {
open();
setTimeout(close, 5000);
}
function open() {
var $this = $('.slidebttn');
var $slidelem = $this.prev();
$slidelem.stop().animate({'width':'225px'},800);
$slidelem.find('span').stop(true,true).fadeIn();
$this.addClass('button_c');
}
function close() {
var $this = $('.slidebttn');
var $slidelem = $this.prev();
$slidelem.stop().animate({'width':'70px'},700);
$slidelem.find('span').stop(true,true).fadeOut();
$this.removeClass('button_c');
}
</script>
What is different here is that it's using timeout functions to initialize and close slider. These two function (initialize and close) are separated, so you can use them whenever you want.
You can manipulate the time when you're slider opens and closes.
Opening time is 2000 right now, which means (~2s) and closing is 5000 (~5s).
It will work only when the buttons are available as the element to slide is found using .slidebttn element.

losing variable in function

Could someone tell me why in the first alert(items.index($(this))) = 1 and the second alert(items.index($(this))) = -1. How does this value get changed within the other function?
$(function () {
var items = $('#v-nav>ul>li').each(function () {
$(this).click(function () {
//remove previous class and add it to clicked tab
items.removeClass('current');
$(this).addClass('current');
alert(items.index($(this)));
$('#v-nav>div.tab-content').fadeOut("slow", function () {
alert(items.index($(this)));
$('#v-nav>div.tab-content').eq(items.index($(this))).fadeIn("slow");
});
// window.location.hash = $(this).attr('tab');
});
});
this refers to current object.
In first version,
this is an item of $('#v-nav>ul>li') list.
While in second version,
this is DOM object selected by $('#v-nav>div.tab-content')
If you want to retain the previous value of this, then cache it in a variable.
(Caching $(this) is a very good practise, as you always save a function call).
When you use $(this) you actually passes this into $ function.
$(function () {
var items = $('#v-nav>ul>li').each(function () {
var $this = $(this);
$this.click(function () {
//remove previous class and add it to clicked tab
items.removeClass('current');
$this.addClass('current');
alert(items.index($this));
$('#v-nav>div.tab-content').fadeOut("slow", function () {
alert(items.index($this));
$('#v-nav>div.tab-content').eq(items.index($(this))).fadeIn("slow");
});
// window.location.hash = $(this).attr('tab');
});
});
Inside the callback function for the animation, this is not the element that you clicked, it's the element being animated.
"The callback is not sent any arguments, but this is set to the DOM
element being animated."
http://api.jquery.com/fadeOut/
(And if it hadn't been set to the animated element, it would have been a reference to the window object.)
Copy the reference to a variable outside the animation call:
var t = this;
$('#v-nav>div.tab-content').fadeOut("slow", function () {
alert(items.index($(t)));
$('#v-nav>div.tab-content').eq(items.index($(t))).fadeIn("slow");
});
You have to consider the context of each "this", each of the callbacks has a distinct "this" variable. If you want to keep the original this around, do something like:
var self = this;

How to determine if content in popup window is loaded

I need to set some contextData for a popup window from its parent. I try this:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.contextData = contextData
//w.context data is null in the popup after the page loads - seems to get overwritten/deleted
});
});
It doesn't work, so my next thought, wait until content is loaded
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
w.onload = function() {
//Never Fires
w.contextData = contextData;
}
});
});
See this fiddle. My onload method never fires.
This works:
var some contextData = {};
$(function() {
$('#clickme').click(function() {
var w = window.open('http://jsfiddle.net');
setTimeout(function(){
if(w.someVariableSetByThePageBeingLoaded) {
w.contextData = contextData;
}
else{
setTimeout(arguments.callee, 1);
}
}, 1);
});
});
But has obvious elegance problems (but is the current work around).
I know you can go the other way (have the popup call back to a method on the opener/parent, but this forces me to maintain some way of looking up context (and I have to pass the key to the context to the popup in the query string). The current method lets me capture the context in a closure, making my popup a much more reusable piece of code.
I am not trying to do this cross domain - both the parent and popup are in the same domain, although the parent is an iframe (hard to test with jsfiddle).
Suggestions?
If you are doing this with an iframe try it this way
HTML
<button id="clickme">Click Me</button>
<iframe id="framer"></iframe>
​
Javascript
$(function() {
$('#clickme').click(function() {
$("#framer").attr("src","http://jsfiddle.net");
$("#framer")[0].onload = function(){
alert('loaded');
};
});
});​
I updated your jsfiddle http://jsfiddle.net/HNvn3/2/
EDIT
Since the above is completely wrong this might point you in the right direction but it needs to be tried in the real environment to see if it works.
The global variable frames should be set and if you
window.open("http://jsfiddle.net","child_window");
frames["child_window"] might refer to the other window
I got javascript access errors when trying it in jsfiddle - so this might be the right track
EDIT2
Trying out on my local dev box I was able to make this work
var w = window.open("http://localhost");
w.window.onload = function(){
alert("here");
};
the alert() happened in the parent window

Using multiple instances of setInterval

I have a jsFiddle here: http://jsfiddle.net/dztGA/22/
The goal: Essentially, I'm trying to have 2 discrete timers on the same page that can be destroyed and re-created on mouseover/mouseout (pause), or on manual progression (restart).
The problem: What my jsFiddle's single timer will illustrate is that when I click "Stop Timer", my setInterval (stored in variable t) seems to have multiple instances albeit being destroyed with clearInterval(t). This becomes apparent when I click "Restart Timer" and it seems to have 2+ independent timers as illustrated by the quick increment.
A caveat: I have done as much research on SO as I can, but because I'll be having 2 different sliders on the page, I can't use any "clear all timers" methods, so I tried storing each in a variable.
I hope that's clear. Thanks for the view.
To fix your current issue: Add clearInterval(window.t) at the onclick function of the reset button.
A method to be able to have multiple timers. This requires a certain structure, though.
Fiddle (6 timers!): http://jsfiddle.net/dztGA/27/
(function(){ //Anonymous function, to not leak variables to the global scope
var defaultSpeed = 3000; //Used when missing
var timerSpeed = [500, 1000, 2000, 4000, 8000];
var intervals = [];
function increase(i){
return function(){
var elem = $("#count"+i);
elem.text(parseFloat(elem.text()) + 1);
}
}
function clear(i){
return function(){
clearInterval(intervals[i]);
}
}
function restart(i){ //Start AND restart
return function(){
clear(i)();
increase(i)();
intervals[i] = setInterval(increase(i), timerSpeed[i]||defaultSpeed);
}
}
// Manual increment
$('input[name=increment]').each(function(i){
$(this).click(function(){
restart(i)();
increase(i)();
});
});
// Clear timer on "Clear"
$('input[name=clear]').each(function(i) {
$(this).click(clear(i));
});
// Restart timer on "Restart"
$('input[name=reset]').each(function(i) {
$(this).click(restart(i));
//Optionally, activate each timer:
increase(i)();
});
})();
// Clear timer on "Clear"
$('input[name=clear]').click(function() {
window.clearInterval(t);
});
should be
// Clear timer on "Clear"
$('input[name=clear]').click(function() {
window.clearInterval(window.t);
});
because this is the input not Window

Categories

Resources