We're trying to make sure our JavaScript menu, which loads content, doesn't get overrun with commands before the content in question loads and is unfurled via .show('blind', 500), because then the animations run many times over, and it doesn't look so great. So I've got about six selectors that look like this:
("#center_content:not(:animated)")
And it doesn't seem to be having any effect. Trying only :animated has the expected effect (it never works, because it doesn't start animated), and trying :not(div) also has this effect (because #center_content is a div). For some reason, :not(:animated) seems not to be changing the results, because even when I trigger the selector while the div in question is visibly animated, the code runs. I know I've had success with this sort of thing before, but the difference here eludes me.
$("#center_content:not(:animated)").hide("blind", 500, function () {
var selector_str = 'button[value="' + url + '"]';
//alert(selector_str);
var button = $(selector_str);
//inspectProperties(button);
$("#center_content:not(:animated)").load(url, CenterContentCallback);
if (button) {
$("#navigation .active").removeClass("active");
button.addClass("active");
LoadSubNav(button);
}
});
I hope this provides sufficient context. I feel like the second selector is overkill (since it would only be run if the first selector succeeded), but I don't see how that would cause it to behave in this way.
Here's the snippet that seemed to be working in the other context:
function clearMenus(callback) {
$('[id$="_wrapper"]:visible:not(:animated)').hide("blind", 500, function() {
$('[id^="edit_"]:visible:not(:animated)').hide("slide", 200, function() {
callback();
});
});
}
Here, the animations queue instead of interrupt each other, but it occurs to me that the selector still doesn't seem to be working - the animations and associated loading events shouldn't be running at all, because the selectors should fail. While the queueing is nice behavior for animations to display, it made me realize that I seem to have never gotten this selector to work. Am I missing something?
Sometimes it's helpful to use .stop() and stop the current animation before you start the new animation.
$("#center_content").stop().hide("blind", 500, function () {});
Really depends on how it behaves within your environment. Remember that .stop() will stop the animation as it was (eg. halfway through hiding or fading)
I don't know if I understand it correctly, but if you want to make sure the user doesn't trigger the menu animation again while it's currently animating(causing it to queue animations and look retarded, this works and should help. I use an if-statement. And before any mouseover/off animation I add .stop(false, true).
$('whatever').click(function(){
//if center_content is not currently animated, do this:
if ($("#center_content").not(":animated")) {
$(this).hide(etc. etc. etc.)
}
//else if center_content IS currently animated, do nothing.
else {
return false;}
});
another example i found elsewhere:
if($("#someElement").is(":animated")) {
...
}
if($("#someElement:animated").length) {
...
}
// etc
then you can do:
$("#showBtn").attr("disabled", $("#someElement").is(":animated"));
Related
I'm trying to create a generic function that can be placed just once in my site and work across multiple pages, nice and lightweight.
I want to be able to make certain divs on the site fade-in when you reach 10px above them on the scroll.
I want to do this by simply adding the following attributes to my divs:
.fade-in-block
#specific-block-name
The idea is that I could go through the site, add this class and an ID, and the animation would work.
I almost have it working except for one thing, the scroll listening constantly continues to console.log after the function has been called. I don't like this as it feels like it's going to be constantly trying to apply the animation, which won't really be seen from the front-end but I feel the constant maths behind the scenes could slow stuff down.
Here is my jQuery:
$('body .fade-in-block').each(function(){
var block = '#'+$(this).attr('id');
console.log('Block class is = '+block);
var offset = $(block).offset().top;
var $w = $(window).scroll(function () {
if ($w.scrollTop() > offset - 10) {
console.log('reached block turn-on point for '+block);
$(block).removeAttr('id'); // remove the ID from the element so the script doesn't continue to find the element
// fade and rise animation here
}
});
});
And here is a JSFiddle. It works just fine, but once you hit the block you'll see it logs constantly every pixel scrolled.
I tried to remedy this by removing the selecting id from the element once the event has occurred, but it continues to run.
Scroll and resize events both have this problem and the solution is said to be debouncing. However, I've never actually gotten debouncing to work properly. Instead I typically create a sort of switch that is turned off once the scroll condition has activated. In your case, since you have multiple elements, you would need to assign a switch to each element.
$(window).on('scroll', function(){
$('.fade-in-block').each(function(){
var appear = $(this).attr('data-appeared');
if(!appear){
$(this).attr('data-appeared', true);
//do something to $(this)
}
})
})
Here I'm adding a data attribute after it has appeared and checking for it again once it has.
I want to replay my jquery function ChangeStats() every 5 seconds, it's currently doing sod all.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500);
setTimeout(function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
}, 500);
}
$(document).ready(function(){
setInterval(ChangeStats, 5000);
})();
Yes I have got the right class names.
No I haven't used underscores in my HTML.
I think it's something to do with my use of "find()", once the DOM has loaded and the function is set is it meant to traverse up the DOM tree instead of down?
EDIT:
Updated code, still not working.
HTML:
<span class="admin-stats-big-figures">%productCount%</span>
<span class="admin-stats-big-figures-hidden">hey</span>
Ok, I am going to go out on a limb and make several assumptions here; one is that you wish to cycle between two elements repeatedly, another is that you are using $(this) in the context of the window rather than a containing element. If either of these are incorrect then the following solution may not be suitable. However, let's give this a shot, eh?
1) You need to use setInterval rather than setTimeout to create a repeating call. You can of course "chain" your timeouts (ie: call the succeeding timeout from the code of the current timeout). This has some benefits in certain situations, but for now let's just assume you will use intervals rather than timeouts.
2) You call the find() jQuery method every time, which is a little unnecessary, especially if you will be repeating the actions so one idea would be to cache the lookup. If you are going to do that a custom object would be more suitable than separate global variables.
3) Some flexibility in terms of starting and stopping the animation could be provided. If we use a custom object as mentioned in (2) then that can easily be added.
4) You are using fadeIn and fadeOut, however if you wish the items to cycle then fadeToggle may be your best solution as it will simply allow you to do exactly that, toggle, without needing to check the current opacity state of the element.
5) Finally in my example I have provided a little extra "padding HTML" in order for the example to look good when run. Fading in jQuery will actually set the faded item to a CSS display of "none" which results in the content "jumping about" in this demo, so I have used some div's and a couple of HTML entity spaces to keep the formatting.
Ok, after all that here is the code..
// your custom animation object
var myAnim = {
// these will be cached variables used in the animation
elements : null,
interval : null,
// default values for fading and anim delays are set to allow them to be optional
delay : { fade: 500, anim: 200 },
// call the init() function in order to set the variables and trigger the animation
init : function(classNameOne, classNameTwo, fadeDelay, animDelay) {
this.elements = [$("."+classNameOne),$("."+classNameTwo)];
// if no fade and animation delays are provided (or if they are 0) the default ones are used
if (animDelay) this.delay.anim = animDelay;
if (fadeDelay) this.delay.fade= fadeDelay;
this.elements[0].fadeOut(function(){myAnim.start()});
},
// this is where the actual toggling happens, it uses the fadeToggle callback function to fade in/out one element once the previous fade has completed
update : function() {
this.elements[0].fadeToggle(this.delay.anim,function(el,delay){el.fadeToggle(delay)}(this.elements[1],this.delay.anim));
},
// the start() method allows you to (re)start the animation
start : function() {
if (this.interval) return; // do nothing if the animation is currently running
this.interval = setInterval(function(){myAnim.update()},this.delay.fade);
},
// and as you would expect the stop() stops it.
stop : function () {
if (!this.interval) return; // do nothing if the animation had already stopped
clearInterval(this.interval);
this.interval = null;
}
}
// this is the jQuery hook in order to run the animation the moment the document is ready
$(document).ready(
function(){
// the first two parameters are the two classnames of the elements
// the last two parameters are the delay between the animation repeating and the time taken for each animation (fade) to happen. The first one should always be bigger
myAnim.init("admin-stats-big-figures","admin-stats-big-figures-hidden",500,200);
}
);
OK, so now we need the HTML to compliment this (as I say I have added a little formatting):
<div><span class="admin-stats-big-figures">One</span> </div>
<div><span class="admin-stats-big-figures-hidden">Two</span> </div>
<hr/>
<input type="button" value="Start" onclick="myAnim.start()"/> | <input type="button" value="Stop" onclick="myAnim.stop()"/>
I have also provided buttons to stop/start the animation. You can see a working example at this JSFiddle - although the stop/start buttons are not working (presumably something specific to JSFiddle) they do work when in context though.
Here im gonna just replace your $(this). and maybe it'll work then.. also using callback.
function ChangeStats() {
$('body').find('.admin-stats-big-figures-hidden').fadeIn(500, function() {
$('body').find('.admin-stats-big-figures').fadeOut(500);
});
}
$(document).ready(function(){
setTimeout('ChangeStats()', 5000);
});
This is probably basic to most reading, but I can't seem to figure it out.
I have a little test function that I want to execute if under a certain width. When the screen rotates or gets resized above that width, I want the function to cease to work. Here is some example code for simplicity sake.
enquire.register("screen and (max-width:500px)",{
match : function() {
$(".block .block-title").click(function(){
alert("Hello World!");
});
}
}).listen();
So if the page loads above 500px, it works as intended. Clicking won't execute. If the page loads at 500px or below, the click function executes. Only problem is that if you resize the viewport or change orientation to something above 500px, the function still executes. I'd like to be able to disable that.
The real world scenario I'm actually trying to do here is I have an un-ordered list of 4 items. Above a certain width they are displayed right away. If under a certain width, I just want to hide them and on click show them. I know there are a few ways to do it (.toggle(), .toggleClass("myclass"), etc).
I have done this a bunch of times but I always get caught with the entering / exiting break points and things not being reset, or working as intended. Usually it doesn't matter, but lately in some of my use cases it has mattered.
I know of the unmatch option but I'm not sure how to really kill the matched function above.
enquire.register("screen and (max-width:500px)",{
match : function() {
$(".block .block-title").click(function(){
alert("Hello World!");
});
},
{
unmatch : function() {
// what do I do here do kill above?
}
}
}).listen();
Any help would be appreciated. I am pretty sure it will help my current situation but will also help me expand my knowledge of enquire.js for other things.
Thanks.
edit: I forgot to mention... if you load the page under 500px, then resize or orientate wider then 500px, then go BACK under 500px, the click function won't work again.. which confuses me also. I basically was hoping it would work no matter what when under 500px, and not work at all when over 500px.
I'm the author of enquire.js, so hopefully I'll be able to help you ;-)
Basically, you want to add an event handler on match and remove event handler on unmatch. You seem to have the gist of how to do this above, but you've got the syntax a little wrong. Once the syntax is corrected it's just some jQuery knowledge to remove the click handler.
So let's look at how the syntax should be:
enquire.register("screen and (max-width:500px)", {
match: function() {
//match code here
},
unmatch: function() {
//unmatch code here
}
}).listen();
Notice that match and unmatch are part of a single object supplied to register.
Ideally you should be putting this in your document ready callback. To assign your click handler use jQuery's on method, as this allows you to use the off method to unassign:
$(".block .block-title").on("click", function() {
alert("hello");
});
$(".block .block-title").off("click");
This is great because you can even namespace your events, read up on the jQuery docs for more details on this. So to put it all together, we would have this:
$(document).ready(function() {
var $target = $(".block .block-title");
enquire.register("screen and (max-width:500px)", {
match: function() {
$target.on("click", function() {
alert("Hello World!");
});
},
unmatch: function() {
$target.off("click");
}
}).listen();
});
You can find a working example here: http://jsfiddle.net/WickyNilliams/EHKQj/
That should then be all you need :) Hope that helps!
I'm looking for some help to implent a timer for this script I'm linking to.
As it is now, it toggles different slides when hovering the list to the right, but I want the slider to automatically jump ahead to the next slide after a certain amount of time until it reaches the end and then goes back to the top.
The catch though is that it also needs to work as it is now, so that you can toggle via hovering and when you stop hovering it should remember the position and jump ahead to the next item.
I realize this is alot to ask for, but some pointer would be great, thanks alot!
DEMO: http://jsbin.com/acorah
Your code is taking a bit of a performance hit with that each() loop which I don't think you need. You're binding events inside the loop and you're limiting your possibilities by declaring your actions inside the bind() scope. You want to be able to call events on any object and not only a single element; $('.cn_item') in your case.
The idea is to keep track of your current slide with a class, let's say .cur.
Then you create an object where you declare all your methods. The main methods or actions are getCur() and goTo() and mostly everything else will use these. ie. next() is just a shortcut for goTo()
var actions = {
getCur: function(){ return idx; },
goTo: function(idx){
// The simplest case
$slides.hide().eq(idx).show();
},
next: function(){ this.goTo(this.getCur()+1); },
prev: function(){ this.goTo(this.getCur()-1); }
.
.
.
}
Now you can call actions on events by simply doing this:
$slides.click(function(){ actions.goTo($(this).index()); });
$next.click(function(){ actions.next(); });
And then you can setInterval() to add a timer.
setInterval(actions.next, 1000);
This tutorial might help. I basically cover everything involved in making a slider. I would change some things as of today, we learn new ways to code stuff everyday.
I'm required to develop a slideshow (not an existing one) with jQuery. I was able to change picture with a function that I created named changePic (takes an image link). It incorporates the fading animation from the jQuery library.
For the slideshow I'm trying to use a while loop. It kind of works, except that it doesn't wait for the animation to finish.
How do I, a) wait for the animation to finish, b) delay the changing picture so it display the picture for a couple of seconds?
Also tried Settimeout, and it doesn't work.
Edit:
Basically changing image is like this:
function changePic(imglink){
var imgnode = document.getElementById("galleryimg");
$(imgnode).fadeTo(500, 0, function(){
$(imgnode).attr("src", imglink);
$(imgnode).fadeTo(1000, 1);
})
}
and the slideshow code is like this, but obviously it shouldn't.
function slideshow(gallerylinks){
var i=0;
while (i<gallerylinks.length){
changePic(gallerylinks[i]);
i++;
}
}
You could always try ditching the while loop, and going with a perpetually recursive function...
on the .animate, you could add a timeout function (at whatever interval) that calls the changePic function. As I have no idea what your code looks like, I will provide a fantastically generic outline.
/* array of imgUrls */
var imgUrls = new Array(); //populate it however
changePic(slideToShowIndex, fadeOutSpeed, fadeInSpeed, slideDelay)
{
$('#slideHolder').animate({ opacity: 0}, fadeOutSpeed , function(){
$('#slideHolder').attr('src', imgUrls[slideToShowIndex]);
$('#slideHolder').animate({ opacity: 1 }, fadeInSpeed, function() {
setTimeout(function() { changePic(slideToShowIndex+1, fadeOutSpeed, fadeInSpeed, slideDelay);}, slideDelay});
});
}});
}
$(document).ready(function() {
changePic(0, 5000, 5000, 10000);
});
This should (in theory) fade the image out, swap it with the new one, and fade it in (both taking 5 seconds) and then adding a delay to call itself with the next slide index in 10 seconds.
This is in no way perfect, but does outline the general idea. Since we have no idea what your code looks like, I can only assume your setTimeout was in the wrong spot. Doing it like this will make sure that the animation has finished before the timeout is set. This guarantees that the slide wont change until after the animation has changed.
of course you could always use a combination of the ':not(:animated)' selector and a setInterval to achieve much the same effect.
EDIT: made a slight change to stack the animations properly. The thoery behind this still works even with the OPs addition of code.
You could have provided more details or example code but have a look at stop() and delay() functions.