I am trying to create an animation for a button on click event. A simple function which simply consists on toggling classes and setting timeouts is used for the animation.
It works well for one button but when I have more than one button and I click two or more of them consecutively before the animation is finished, the animation stops and continues on the element which has been clicked later.
So the problem is to make the animation function to refer to the object which has triggered it, therefore creating multiple instances of it, for which I haven't been able to find a simple solution after hours of search).
Thanks in advance.
There's a simplified example (real example has more classes toggles):
$('.myButton').on('click', animateButton);
function animateButton(){
var $this = $(this);
$this.addClass('animate');
setTimeout(function(){
$this.removeClass('animate');
},2000)
}
EDIT: I've made a fiddle: https://jsfiddle.net/8ozu14am/
EDIT2: SOLVED
Using $.proxy() it is possible to maintain the context.
$('.myButton').on('click', animateButton);
function animateButton(){
$(this).addClass('animate');
setTimeout($.proxy(function(){
$(this).removeClass('animate');
},this),2000)
}
Jquery return an Event to your handler.
It have the property targetwhich is the DOM element that initiated the event.
$('.myButton').on('click', animateButton);
function animateButton(evt){
var $this = $(evt.target);
$this.addClass('animate');
setTimeout(function(){
$this.removeClass('animate');
},2000)
}
SOLVED
Using $.proxy() it is possible to maintain the context.
https://api.jquery.com/jQuery.proxy/
$('.myButton').on('click', animateButton);
function animateButton(){
$(this).addClass('animate');
setTimeout($.proxy(function(){
$(this).removeClass('animate');
},this),2000)
}
Related
I'm trying to create a simple game that has a function that selects a random div(out of a selection)and then sets a random countdown using an interval and when the countdown hits 0 the class of that div will change.
Then i have it so when you click on something assigned with that class it will change back to the original class.
At the moment when i'm running my code the divs seem to be changing after the countdown but won't change when i click them.
But my main problem is that the main function that changes the random divs is only running once.
The divs ("box") start as .wait
My code:
var react = function(){
var box = parseInt(Math.random()*64);
while($("box"+box).hasClass("now")) {
box = parseInt(Math.random()*64);
}
var timer = parseInt((Math.random()*10)+2);
var countdown=setInterval(function(){
timer-=1
$("#box"+box).text(parseFloat(timer.toFixed(0)));
if(timer<=0){
clearInterval(countdown)
$("#box"+box).text("");
$("#box"+box).text("");
$("#box"+box).removeClass("wait");
$("#box"+box).addClass("now");
}
},1000)
}
$(document).ready(function(){
//paint\\
//$(".wait").click(function() {
//$(this).toggleClass("now")
//})
//paint\\
setInterval(react(),1000);
$(".now").click(function(){
$(this).removeClass("now");
$(this).addClass("wait");
})
})
The issue is how you're binding your click event. You'll want to delegate that event, rather than use click().
When setInterval runs, it adds a class, 'new' to an element. However, since no elements had that class name (when calling click() in $doc.ready), no handler is triggered.
First, a fiddle demonstrating this works: http://jsfiddle.net/yvvMp/
Here's an example using your code + delegating the events:
var react = function(){
var box = parseInt(Math.random()*64);
while($("#box"+box).hasClass("now")) {
box = parseInt(Math.random()*64);
}
var timer = parseInt((Math.random()*10)+2);
var countdown=setInterval(function(){
var $el = $('#box' + box);
timer-=1
$el.text(parseFloat(timer.toFixed(0)));
if(timer<=0){
clearInterval(countdown);
$el.text("")
.removeClass("wait")
.addClass("now");
}
},1000);
}
$(document).ready(function(){
$parent = $('.parent-to-now-elements') // $('body') works, but not as efficient
setInterval(react, 1000);
$parent.on('click', '.now', function(){
$(this).removeClass("now");
$(this).addClass("wait");
})
})
Tilwin's answer will work, but you run into the chance that the same element could have multiple event handlers bound. Depending on how long the game runs, and how often a user gets the same DIV element randomly selected, your DOM could look something like:
<div class='wait wait wait wait wait wait wait wait wait'></div>
Worse, each time jQuery calls the click handler, you're forcing the browser to touch the DOM (depending on the game, this could be bad!)
Here's an example: http://jsfiddle.net/pjMcv/
(When a block turns green, click it. Then wait for it to turn red again and click...)
(Tilwin has edited his answer, removing .click out of setInterval. His edited answer is better, but it still has a downside - it requires n number of event bindings. Works for simple games, but if your game has 1000 squares, you'll have 1000 event handlers)
"But my main problem is that the main function that changes the random divs is only running once."
i'm assuming that you're referring to the following line.
setInterval(react(), 1000);
modify it as follows:
setInterval(react, 1000);
update:
assuming you've a fixed number of div, you can assign a common handler for all of them at page load like
$(document).ready(function(){
$('your-common-div-selector').click(function(){
if($(this).hasClass("now")&& !$(this).hasClass("wait")) {
this.removeClass("now");
this.addClass("wait");
});
});
I want to toggle text between bold and normal I made this code for it, but when I open my page the bold button disappears?
$("#bold").toggle(function() {
$('.focus').css("font-weight", $(this).val());
}, function() {
$('.focus').css("font-weight", "normal");
});
Is there something wrong with my code?
Please help, thanks in advance.
Assuming you're using jQuery 1.9 or later the problem is that the .toggle() event handling method was removed from the library. So what you're actually calling is the .toggle() function that hides/shows elements. (In earlier versions of jQuery both functions existed and jQuery figured out which one you meant based on the arguments passed in.)
You can implement your own toggle easily enough with a standard .click() handler:
$("#bold").click(function() {
var f = !$(this).data("toggleFlag");
if (f) {
$('.focus').css("font-weight", $(this).val());
} else {
$('.focus').css("font-weight", "normal");
}
$(this).data("toggleFlag", f);
});
This uses the .data() method to keep track of a boolean flag to indicate which code to execute. The very first time the click handler is called the flag will be returned as undefined because it hasn't previously been set, but we just convert that to a boolean with ! (assuming you want to execute the if and not the else case on the first click).
It disappears because that version of toggle is deprecated and removed, and in newer versions of jQuery all it does is toggle visibility.
You could do something like this instead :
var state = true;
$("#bold").on('click', function() {
$('.focus').css("font-weight", state ? this.value : 'normal');
state = !state;
});
FIDDLE
The only solution I fount to the disappearing element after click... is Callback function after the toggle effect finished.
here a link that explain the Callback function.
and here is my code:
jQuery('.menu li.item-487').click(function(){
jQuery('#main-menu .moduletable .menu li').toggle("slow",function(){jQuery('.menu li.item-487').css('display' , 'block');});
});
Interesting one for me. I've got a video player whos controls are showed on hover. Initially, I did this with CSS but had to change strategy to javascript to play nice with browsers fullscreen api (which, incidentally means you're always hovering on the video).
My new approach is setting an event handler for mousemove for the video's container that adds a class and sets a timeout to remove it, and if the timeout is already set, clears it. This works perfectly, but the logic doesn't scale to more than one player.
TLDR: how can I expand my logic to scale to more than one video container? The scope of the time variable needs to be contained to each elements event, but also outside of the handler so that it can be cleared out from one event to the next (on the same element).
Thanks for your help- these logic questions are always difficult for me.
Here's a jsFiddle example. You'll notice that it works well when you limit hovering to one element, but there are issues when you expand to the rest of the elements on the page.
HTML:
<div class="cont">
<div class="controls">controls</div>
</div>
JavaScript:
var time;
$('body').on('mousemove', '.cont', function(){
var thiis = $(this);
if (time){
clearTimeout(time);
}
thiis.addClass('showControls');
time = setTimeout(function(){
thiis.removeClass('showControls');
}, 2000);
});
You could store the time variable using jQuery's data method which can store data on each of your elements.
$('body').on('mousemove', '.cont', function(){
var thiis = $(this),
// get the time from the data method
time = thiis.data("timer-id");
if (time){
clearTimeout(time);
}
thiis.addClass('showControls');
var new_time = setTimeout(function(){
thiis.removeClass('showControls');
}, 2000);
// save the new time
thiis.data("timer-id", new_time);
});
I have a page with photo gallery http://dev.dolina-imeniy.ru/fotogalereya/kp_usadba_tishnevo/
I use this to bind click event and return it false
$('a.link_photo').click(function(e) {
var new_img = $(this).attr('href');
var photo_title = $(this).attr('title');
var photo_info = $('.photo_info', this).html();
$('#photo_view img').attr({
src : new_img
});
$('#photo_title').html(photo_title);
$('#photo_info').html(photo_info);
return false;
});
But on some images it not work! Why it appears?
Try click on 10 image (ut-1-foto.jpg) in my example to see it.
For some reason you code is breaking, so it does not reach to return false.
You can use e.preventDefault(); to stop the default action
e.preventDefault();
The reason for this is that the function only binds to the elements that are already in existent when it is called. Every link created after the the document has loaded will not be bound to this function. To listen for the creation of these elements and to then bind the function to them, you could use the jQuery plugin liveQuery. I hope that helps.
trying calling e.preventDefault()
For more info look here:
http://api.jquery.com/event.preventDefault/
I don't think return false; or event.preventDefault() has anything to do with it. I'm guessing it has to do with how your carousel works. It's no coincidence that your code breaks once the images start repeating - the click event is probably no longer bound. If the element is just being moved, the events should still be set, but if it's being cloned or copied the events might not be.
edit: I can confirm by debugging that your script isn't even called on the 'broken' links.
I currently have several elements in a row that have a mouseover event that fires some animation. My problem is that if someone mouses over several of the elements in quick succession the animation gets a little frantic.
I'm curious if there is a way to have a mouseover event that only fires if the mouse is over an element for a certain amount of time (say 250 milliseconds). Can this be done with jQuery?
I would suggest you use setTimeout for this:
(function ($) {
var t;
$('ul li').hover(function() {
var that = this;
window.clearTimeout(t);
t = window.setTimeout(function () {
$(that).animate({opacity: .5}, 'slow').animate({opacity: 1});
}, 250);
});
}(jQuery));
If there are multiple items activated in rapid succession the timeout will override the timeout-id thus preventing the first item that should not start from animating.
It does not require any arcane plugin (although hoverIntent may provide some nice additional features you may want to use) and window.setTimeout is supported everywhere.
UPDATE
I updated the code sample to work.. was writing this from memory yesterday and didn't get the setTimeout call quite right.. Also see this jsFiddle for reference.
The issue I see with this is that it will execute the hover animation even if you leave the . So you could also add a $('ul').mouseleave(function() { window.clearTimeout(t) }); to prevent that.
greetings Daniel
I suggest that you check out the jQuery HoverIntent plugin ( 1.4k minified ). Here's the link: http://cherne.net/brian/resources/jquery.hoverIntent.html. It's a great plugin, I've used it many times!
Here's a small sampling of code:
var config = {
over: makeTall, // function = onMouseOver callback (REQUIRED)
timeout: 500, // number = milliseconds delay before onMouseOut
out: makeShort // function = onMouseOut callback (REQUIRED)
};
$("#demo3 li").hoverIntent( config )
yes:
to accomplish this put a setTimeout in your onMouseover function and a clearTimeout on mouseout
You may need a little more logic, but that's the nuts and bolts of it
here's an example of stop() in action, hope that will help:
without stop():
http://jsfiddle.net/5djzM/
with stop() cleaning the queue of animations:
http://jsfiddle.net/KjybD/