clearTimeout not working in nested function - javascript

I think it's got something to do with the nested functions, but they need to be this way. Why isn't it working? Am I doing something stupid? This is an isolated example and I must be using $(this), so it seems I have to nest the functions?
HTML:
<div class="box"></div>
<ul>
<li>Hover box, it turns blue. Leave box, it turns red after 2 secs.</li>
<li>If you hover back onto box before 2 secs is up, it's supposed to clear timer and keep box blue.</li>
<li>It doesn't clear timer and after 2 secs the box still turns red. Why?</li>
</ul>
JavaScript:
var t;
$('.box').on('mouseenter', function() {
$thisBox = $(this);
clearTimeout(t);
$thisBox.addClass('blue');
$thisBox.on('mouseleave', function() {
t = setTimeout(function() { $thisBox.removeClass('blue'); }, 2000);
})
});
​JSFiddle: http://jsfiddle.net/ddbtZ/7/
Thanks for looking :)

http://jsfiddle.net/ddbtZ/3/
Your .on() shouldn't be nested. Effectively, that's attaching a new handler for every time you hover over the element.
EDIT: As per question clarification.
Use .one() instead of .on()
http://jsfiddle.net/ddbtZ/8/

Move your mouseleave from mouseenter event and it will work.
var t;
$('.box').on('mouseenter', function() {
clearTimeout(t);
$('.box').addClass('blue');
});
$('.box').on('mouseleave', function() {
t = setTimeout(function() {
$('.box').removeClass('blue');
}, 2000);
})​;
DEMO: http://jsfiddle.net/ddbtZ/4/

Related

How do I detect when a div has lost focus?

Given the following markup, I want to detect when an editor has lost focus:
<div class="editor">
<input type="text"/>
<input type="text"/>
</div>
<div class="editor">
<input type="text"/>
<input type="text"/>
</div>
<button>GO</button>
EDIT: As the user tabs through the input elements and as each editor div loses focus (meaning they tabbed outside the div) add the loading class to the div that lost focus.
This bit of jquery is what I expected to work, but it does nothing:
$(".editor")
.blur(function(){
$(this).addClass("loading");
});
This seems to work, until you add the console log and realize it is triggering on every focusout of the inputs.
$('div.editor input').focus( function() {
$(this).parent()
.addClass("focused")
.focusout(function() {
console.log('focusout');
$(this).removeClass("focused")
.addClass("loading");
});
});
Here is a jsfiddle of my test case that I have been working on. I know I am missing something fundamental here. Can some one enlighten me?
EDIT: After some of the comments below, I have this almost working the way I want it. The problem now is detecting when focus changes to somewhere outside an editor div. Here is my current implementation:
function loadData() {
console.log('loading data for editor ' + $(this).attr('id'));
var $editor = $(this).removeClass('loaded')
.addClass('loading');
$.post('/echo/json/', {
delay: 2
})
.done(function () {
$editor.removeClass('loading')
.addClass('loaded');
});
}
$('div.editor input').on('focusin', function () {
console.log('focus changed');
$editor = $(this).closest('.editor');
console.log('current editor is ' + $editor.attr('id'));
if (!$editor.hasClass('focused')) {
console.log('switched editors');
$('.editor.focused')
.removeClass('focused')
.each(loadData);
$editor.addClass('focused');
}
})
A bit more complicated, and using classes for state. I have also added in the next bit of complexity which is to make an async call out when an editor loses focus. Here a my jsfiddle of my current work.
If you wish to treat entry and exit of the pairs of inputs as if they were combined into a single control, you need to see if the element gaining focus is in the same editor. You can do this be delaying the check by one cycle using a setTimeout of 0 (which waits until all current tasks have completed).
$('div.editor input').focusout(function () {
var $editor = $(this).closest('.editor');
// wait for the new element to be focused
setTimeout(function () {
// See if the new focused element is in the editor
if ($.contains($editor[0], document.activeElement)) {
$editor.addClass("focused").removeClass("loading");
}
else
{
$editor.removeClass("focused").addClass("loading");
}
}, 1);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/8s8ayv52/18/
To complete the puzzle (get your initial green state) you will also need to also catch the focusin event and see if it is coming from the same editor or not (save the previous focused element in a global etc).
Side note: I recently had to write a jQuery plugin that did all this for groups of elements. It generates custom groupfocus and groupblur events to make the rest of the code easier to work with.
Update 1: http://jsfiddle.net/TrueBlueAussie/0y2dvxpf/4/
Based on your new example, you can catch the focusin repeatedly without damage, so tracking the previous focus is not necessary after all. Using my previous setTimeout example resolves the problem you have with clicking outside the divs.
$('div.editor input').focusin(function(){
var $editor = $(this).closest('.editor');
$editor.addClass("focused").removeClass("loading");
}).focusout(function () {
var $editor = $(this).closest('.editor');
// wait for the new element to be focused
setTimeout(function () {
// See if the new focused element is in the editor
if (!$.contains($editor[0], document.activeElement)) {
$editor.removeClass("focused").each(loadData);
}
}, 0);
});
Here's what worked for me:
$(".editor").on("focusout", function() {
var $this = $(this);
setTimeout(function() {
$this.toggleClass("loading", !($this.find(":focus").length));
}, 0);
});
Example:
http://jsfiddle.net/Meligy/Lxm6720k/
I think you can do this. this is an exemple I did. Check it out:
http://jsfiddle.net/igoralves1/j9soL21x/
$( "#divTest" ).focusout(function() {
alert("focusout");
});

Simulate a click with jQuery?

After 5 seconds, I need to simulate a click on a button.
var count = 5;
countdown = setInterval(function () {
$("#count").html(count);
if (count == 0) {
alert("Jotain pitäis tapahtua. kai");
//What should I put instead of this line?
}
count--;
}, 1000);
return false;
Is it even possible?
$('#my_button').trigger('click');
That ought to do it for you.
Or as others have posted the shorthand:
$('#my_button').click();
You can do
$("#button").click();
This triggerses all the event handlers for click event that have been added to the button.Event handlers must have been added by the same instance of jQuery that triggers the click event ( be careful if you have more than one instance of jQuery )
if you want to trigger some namespaced effect use
$("#button").trigger('click.namespace');
From jQuery's .click() documentation:
Description: Bind an event handler to the "click" JavaScript event, or trigger that event on an element.
Yes it is possible, and you appear to be very close to having a complete answer. Try this:
var count = 5;
setInterval(function () {
$("#count").html(count);
if (count == 0) {
alert("Jotain pitäis tapahtua. kai");
$('#myButton').click();
}
count--;
}, 1000);
I just added $('#myButton').click(); to click the button.
why dont you try setTimeout()?
$("targetElement").on("click", function(){
setTimeout(function(){
//do something
},1000);
});

jQuery: If checkbox is clicked animate div

I'm having bug complications in my code. I've been researching for a few hours now for a solution. Here's what I'm trying to achieve:
I have a page with 6 checkboxes. There is a wrapping div .control-case around each checkbox. If 5 checkboxes are checked, the remaining checkbox's .control-case will have a class added .unselected.
NOW, if this wrapper .control-case .unselected is clicked, then I have a notice span that I want to be animated with jQuery color.
Good news: It somewhat works when it's clicked.
Bad News: It sometimes triggers div's without the class every now-and-then and it may also repeat the color changes (I assume due to a queue backup). How do I only allow it to be triggered once on the correct div ONLY when clicked?
P.S. I've also tried the .hasClass method. No success.
Here's my Code:
http://pastebin.com/8XR7iHp2
I appreciate all your support! :)
Each time you run this code,
$('#type-controls .control-case.unselected').click(function() {
A new click handler is actually added to your code. Thus, causing the repeat firing. I have rewritten your code using another method. See if it fulfills your requirement.
$(document).ready(function() {
$('input:checkbox#foam-control, input:checkbox#reversecurve-control, input:checkbox#ultra-control, input:checkbox#pro-control, input:checkbox#icebreaker-control').attr('checked', true);
$("#type-controls input[type=checkbox]").click(function() {
var countchecked = $("#type-controls input[type=checkbox]:checked").length;
if (countchecked >= 5) {
$('#type-controls input[type=checkbox]').not(':checked').attr("disabled", true);
}
else {
$('#type-controls input[type=checkbox]').not(':checked').attr("disabled", false);
}
});
$("#type-controls label").click(function() {
var countchecked = $("#type-controls input[type=checkbox]:checked").length;
if (countchecked >= 5 && $(this).parent().hasClass('unselected')) {
alert("Do notice");
$('.max-notice').animate({
'backgroundColor': '#c30c08',
'color': '#fff'
}, 400).delay(3000).animate({
'backgroundColor': '#fff',
'color': '#777'
}, 300);
}
});
});​
You can view the demo at http://jsfiddle.net/w7djF/1/

Jquery change values mouseover

I wrote this code but it doesn't work:
JavaScript:
$(function() {
var menu_h_number=5
for (i=1; i<=menu_h_number; i++)
{
$(".web_header_mb_"+i).show(1000);
$(".web_header_mb_"+i).css("background", "#FF0000");
$(".web_header_mb_"+i).hover(function ()
{
$(".web_header_mb_"+i).css("width", "200");
});
$(".web_header_mb_"+i).mouseout(function ()
{
$(".web_header_mb_"+i).css("width", "300");
});
}
});
HTML:
<div id="menu" class="web_header_mb_1"></div>
<div id="menu" class="web_header_mb_2"></div>
<div id="menu" class="web_header_mb_3"></div>
<div id="menu" class="web_header_mb_4"></div>
<div id="menu" class="web_header_mb_5"></div>
When start show different ids in the bucle but when I do a mouseover, there's no change to the size.
Why it doesn't work
The reason your code doesn't work is this:
i will have the correct value for code that is executed immediately (e.g. the show and hover calls). But, because of the way JavaScript works, this doesn't work for callback (such as the one you give to hover). JavaScript will remember the variable, not the value of the variable at the time the callback was provided. The callback won't be called until after the loop is completed. That's why in the callbacks i will always be 5, because that was i's last value.
You can read more about that here: Closures (MDN)
Also, be aware that id's must be unique. You can't give the id "menu" to five different elements; that's what classes are for. In other words: you've got id and class backwards in your code.
How to make it work
The easiest way to circumvent the closure "problem" is to use $(this) inside the callback functions. In jQuery, the this keyword inside a callback function always points to the object which triggered the event. By using $(this) you have exactly the right jQuery object, without any fuss:
for (i=1; i<=menu_h_number; i++)
{
var currentItem = $(".web_header_mb_" + i);
currentItem
.show(1000)
.css("background", "#FF0000");
.hover(
function() { // mouseenter
$(this).css("width", 200); // <--
},
function() { // mouseleave
$(this).css("width", 300); // <--
});
}
Another thing I did in the code above is buffer the jQuery object in a local variable (currentItem). This makes your code faster, because you only have to look up the element once (instead of 6 times, in this case). You should do this as much as possible.
Also, as you can see, the hover function isn't just for the mouseover event. You can give it callbacks to handle both mouseover and mouseout.
One other thing you could do, as others have already suggested, is use a single class instead of 5 different classes. The jQuery function ($()) will actually return a collection if the query matches more than one object.
So, given the following HTML:
<div class="menu web_header_mb"></div>
<div class="menu web_header_mb"></div>
<div class="menu web_header_mb"></div>
<div class="menu web_header_mb"></div>
<div class="menu web_header_mb"></div>
You could use each(), like this:
$(".menu.web_header_mb").each(function() {
$(this)
.show(1000)
.css("background", "#FF0000");
.hover(
function() { // mouseenter
$(this).css("width", 200);
},
function() { // mouseleave
$(this).css("width", 300);
});
});
Or even this:
$(".menu.web_header_mb").
.show(1000)
.css("background", "#FF0000");
.hover(
function() { // mouseenter
$(this).css("width", 200);
},
function() { // mouseleave
$(this).css("width", 300);
});
That last one works because show(), css() and hover() all work on jQuery collections (as well as single jQuery objects). Neat, huh?
Its because the i variable is not in scope or has the latest value when the hover code is executed. Instead use this variable.
Fyi:
1. your div tag are having same ID but different classname. Instead, make them same class and different ID. Than you can make use of jQuery.each function very nicely.
hover function can have 2 arguments, first for mousein and second for mouseout. That way you can concise your code
try to use this
$(function(){
var menu_h_number=5;
for (var i=1; i <= menu_h_number;i++) {
$(".web_header_mb_"+i).show(1000)
.css("background","#FF0000")
.mouseover(function () {
$(this).css("width","200");
})
.mouseout(function () {
$(this).css("width","300");
});
}
});
Also available on jsfiddle
jQuery.hover() actually takes two arguements, mouse in and mouse out.
$(".web_header_mb_"+i).hover(
function () { $(this).css("width","200px") },
function () { $(this).css("width","300px") }
);
Actually, looking at the code, that's not really a good way of doing it. Here try it this way: Gave all the divs the same class instead iterating with a for loop, and use the $.each to give the desired events.
<div id="menu" class="web_header_mb"></div>
<div id="menu" class="web_header_mb"></div>
<div id="menu" class="web_header_mb"></div>
<div id="menu" class="web_header_mb"></div>
<div id="menu" class="web_header_mb"></div>
$.each(".web_header_mb", function (){
$(this).hover(
function () { $(this).css("width","200px") },
function () { $(this).css("width","300px") }
);
});
There's no need to loop over elements, jQuery does that for you if you just figure out the selector.
Try matching every element that has a class that starts with web_header_mb_ and then just remove elements that you don't want. If they have the right index you can :lt(5) or slice(0,5), otherwise you'll have to filter them based on the last character of the class. You can also chain methods, no need to call the selector every time :
$("[class^='web_header_mb_']").filter(function() {
var C = $(this).prop('class');
C = C.charAt(C.length-1);
return (C==1||C==2||C==3||C==4||C==5);
}).show(1000)
.css("background","#FF0000")
.on('mouseenter mouseleave', function() {
$(this).css('width', e.type==='mouseenter'?'200':300);
});
or
$("[class^='web_header_mb_']:lt(5)").show(1000)
.css("background","#FF0000")
.on('mouseenter mouseleave', function() {
$(this).css('width', e.type==='mouseenter'?'200':300);
});

jquery simple swap p element?

Given a p element like so:
<p>Something</p> I want to, when the user mouses over it, we have, instead <p>go here</p>
After hovering if the mouse leaves the p area, return to the previous:
<p>Something</p> state.
Can I have a simple example of something like this?
Thanks a lot,
MEM
Or a simple modification of Ken Redler's original that uses .data() to keep track of things:
$('p#someID').hover(
function() {
var $this = $(this);
$this.data('orig', $this.html()).html('go here');
},
function() {
var $this = $(this);
$this.html($this.data('orig')).removeData('orig');
}
);
http://www.jsfiddle.net/ambiguous/FhET2/1/
Updated: As #Phrogz points out, I missed part of the sense of the question. The suggestions of capturing state with a closure or using data() are good ones, but here's another way (with an amusing number of moving parts):
$(document).ready( function(){
$('p#someID').hover(
function() { // on mouseEnter
$(this)
.contents()
.wrap('<span class="hide"/>') // wrap 'n' hide
.end() // back to 'this'
.append('Fascinating Link!'); // add link
}, function() { // on mouseLeave
$(this)
.find('a')
.remove() // kill the anchor
.end() // back to 'this'
.find('span.hide') // the wrapper
.contents() // the wrapped
.unwrap(); // kill the wrapper, leaving its contents
});
});
This assumes a style like so:
span.hide {
display: none;
}
Updated example: http://www.jsfiddle.net/redler/HAGzH/1/
$('p#IDHERE').hover(
function(){ // on mouseEnter
$(this).contents().replaceWith('go here');
},
function(){ // on mouseLeave
$(this).contents().replaceWith("something");
}
);
This will replace all text

Categories