I am writing a small jQuery function and I seem to be having trouble.
What I am trying to do here is when the user scrolls down the page by 90px, a div tag should animate down (from top:-50px to top:0), and vice-versa when they scroll back to the top of the page.
The problem I am having is that the animation seems to be very slow and unresponsive at times. I test in 3 different browsers and different computers but I am having no joy.
Here is my code:
// Show div
var scrollValue = "90";
// Animate functions
var showHead = function (){
$(".element").animate({top: "0"}, 250);
}
var hideHead = function (){
$(".element").animate({top: "-50px"}, 250);
}
$(window).scroll(function() {
if ($(this).scrollTop() > scrollValue) {
showHead();
} else {
hideHead();
}
});
The .element properties:
.element { positoin:fixed; top:-50px; }
Could anyone figure out why my code the hide/showHead functions are so sloppy?
Thanks,
Peter
The scroll event is triggered several times and even though it is rate-limited it keeps being a rather intensive operation. Actually, you may be queuing several animations and the fx stack may be growing very quickly.
One possibility you can try is stopping all previous animations before triggering a new one. You can do this by using .stop().
$(".element").stop().animate({top: "0"}, 250);
The .stop() function also provides some other options which you can use to tweak it even more.
Try this one :
$(window).scroll(function() {
if (window.scrollY > scrollValue) {
showHead();
} else {
hideHead();
}
});
scroll events occurred many time durring user scrolling.
You need to check if your animation is in progress before starting the animation again.
Try this :
// Show div
var scrollValue = "90";
var inProgress = false;
// Animate functions
var showHead = function () {
if(inProgress)
return false;
//Animate only if the animation is not in progress
inProgress = true;
$(".element").animate({
top: "0"
},250,function(){
inProgress = false; //Reset when animation is done
});
}
var hideHead = function () {
if(inProgress)
return false;
//Animate only if the animation is not in progress
inProgress = true;
$(".element").animate({
top: "-50px"
}, 250,function(){
inProgress = false; //Reset when animation is done
});
}
$(window).scroll(function () {
if ($(this).scrollTop() > scrollValue) {
showHead();
} else {
hideHead();
}
});
Assuming you have position:fixed (or some other sort of styling making the bar visible when necessary):
var scrollheight = 90;
var $el = $('.element');
function showHead(){
$el.stop().animate({
top: '0px'
}, 250);
}
function hideHead(){
$el.stop().animate({
top: '-50px'
}, 250);
}
$(window).scroll(function(){
if ($(window).scrollTop() > scrollheight){
showHead();
}else{
hideHead();
}
});
example: http://jsfiddle.net/L4LfL/
try using queue: false and as Alexander said use .stop()
here jsfiddle
http://jsfiddle.net/hwbPz/
Related
I have a problem regarding two animations, one scrolling down and one scrolling up (when the user has scrolled back up completely).
The animations block each other - with the following source code:
jQuery(document).ready(function($) {
$(window).on("load",function() {
$(window).scroll(function(){
if($(window).offset().top == 0){
$("selector").animate({}, 500);
$("selector").animate({}, 500);
} else {
$("selector").animate({}, 500);
$("selector").animate({}, 500);
}
});
}).scroll();
});
So it happens that after the true statement block of the if directly the else part is executed again and therefore the animation is partly not executed at all or both are executed one after the other. Does anyone have an idea that the animations are executed reliably:
else animation once as soon as scrolling down was done
if-Animation as soon as scrolling up completely again
Thanks in advance!
Makes no sense to cauculate $(window).offset().top since... it does what it says. 0.
Perhaps, on "scroll" Event you want to get the $(window).scrollTop()
jQuery($ => {
const $win = $(window);
const $items = $(".item");
$win.on("scroll", () => {
$items.toggleClass("active", $win.scrollTop() == 0);
});
$win.on("load", () => {
$win.trigger("scroll");
});
});
.active {
/* your styles here */
}
I'm hoping a Javascript wiz can help a fellow citizen out with resolving a problem. I've a fairly straight forward function. When I scroll down by 1px I would like to apply a bounceDown class, this will run for 5 seconds and the class will then disappear for future running of the same function.
When I scroll up from that current scroll position I would like the bounceUp effect to apply. However the issue is I think the bounceUp effect only works once you scroll past the original scroll but in addition to this if the previous function is still running on it's 5 second transition then it gets jumpy as it's trying to run two classes at the same time so there almost needs to be a delay applied.
Does anyone think they can help, I'd gratefully appreciate it.
<script>
(function($){
$.fn.extend({
addTemporaryClass: function(className, duration) {
var elements = this;
setTimeout(function() {
elements.removeClass(className);
}, duration);
return this.each(function() {
$(this).addClass(className);
});
}
});
})(jQuery);
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 1) {
$(".spanner").addTemporaryClass("BounceDown", 5000);
}
else if (scroll <= 1) {
$(".spanner").addTemporaryClass("BounceUp", 5000);
}
});
</script>
What about a boolean variable that is 'true' when addTemporaryClass is running? So:
(function($){
var classAdded = false; //New
$.fn.extend({
addTemporaryClass: function(className, duration) {
classAdded = true; //New
var elements = this;
setTimeout(function() {
elements.removeClass(className);
classAdded = false; //New
}, duration);
return this.each(function() {
$(this).addClass(className);
});
}
});
})(jQuery);
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 1 /*New*/ && !classAdded /*New*/) {
$(".spanner").addTemporaryClass("BounceDown", 5000);
}
else if (scroll <= 1 /*New*/ && !classAdded /*New*/) {
$(".spanner").addTemporaryClass("BounceUp", 5000);
}
});
I have some code that is supposed to scroll one page height every time scroll is triggered in some way. I want this to scroll only one heigh, and "pause" the trigger until the scrolling is done. However, my script does not stop, instead it will scroll all the way down instantly. Scrolling upwards seems to work better...
Here is my script:
var lastScroll = 0;
var scrollPos = 0;
var blockScroll = 0;
$(window).scroll(function() {
var scroll = $(this).scrollTop();
if(blockScroll == 0) {
blockScroll = 1;
if(scroll > lastScroll){
// Down
scrollPos++;
console.log(scrollPos+"-"+blockScroll);
$("html, body").animate({scrollTop:$(window).height()*scrollPos}, 'slow', function() {
blockScroll = 0;
});
} else {
// Up
scrollPos--;
console.log(scrollPos);
$("html, body").animate({scrollTop:$(window).height()*scrollPos}, 'slow', function() {
blockScroll = 0;
});
}
}
lastScroll = scroll;
});
blockScroll is meant to be set when a scroll event appears, and to be unset when the scrolling animation stops. As a lock. I am not shure this works the way I want it though... Can someone see something obviously wrong with this? Am I having trouble with the fact that jQuery is async?
It appears that animate is firing scroll events itself, so when it finishes, it's firing one last scroll event which restarts the process.
It seems like adding a small timeout in the callback solves the problem:
setTimeout(function () {blockScroll = 0;}, 50)
http://jsfiddle.net/qch787yq/1/
I need to know the position of an img in real time in order to change its position while is going up or down. The target is animate it to the bottom 40px at the beginning and animate it to the top later.
My code up to now is the following:
var pos = $('header img').offset().top;
if( pos == 0) {
$('header img').animate({"top": "+=40px"}, 6000);
}else{
$('header img').animate({"top": "-=40px"}, 6000);
}
Any idea?
You can use progress option to detect the position live. I also made some changes so you don't have to use so much duplicated code:
$('#startAnimation').on('click', function () {
var pos = $('header #img').offset().top;
if (pos == 0) {
doAnimate("+=40px");
} else {
doAnimate("-=40px");
}
});
function doAnimate(to) {
$('header #img').animate({
top: to
}, {
duration: 6000,
progress: function (promise) {
console.log('offset top: ' + $('header #img').offset().top);
}
});
}
Fiddle: http://jsfiddle.net/z3jk1g0z/
You could also change it to use start and complete options in animate. More information: http://api.jquery.com/animate/
EDIT: wrong Fiddle
What I need to achieve is if we click on submit button, there is particular div should show up.
Here is my code:
http://jsfiddle.net/7tn5d/
But if I click on submit button multiple times, the function calls sort of queue up and run one after other.
Is there a way to invalidate other onclicks when current animation is running?
Code:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", {}, 1000);
animating = 0;
});
});
To prevent it from performing the action multiple times, simple cease the previous animation. So:
$('#submit_cont').stop().show("blind",{},1000);
However, I have noticed that you have attempted to prevent the animation from running, if an animation is already running. Although it takes 1 second or 1000 milliseconds to show the div, the execution of the condition does not pause until the animation is complete. You must define a function to run after the animation is complete, like so:
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() { animation = 0; });
});
});
Hope that helped...
You almost got it right with the semaphore! It's just that, in jQuery's show(), you would have to put the semaphore reset as an argument. Here's the fixed version - http://jsfiddle.net/snikrs/xe5A3/
animating = 0;
doneanim = 0;
$(function () {
$("#submit_tab").click(function (e) {
if (animating == 1) return;
animating = 1;
$("#submit_cont").show("blind", 1000, function() {
animating = 0;
});
});
});
You can use the :animated selector to check:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $("#submit_cont");
if (!$cont.is(':animated')) {
$cont.show("blind", {}, 1000);
}
});
});
Now if you stick with the external semaphore idea then its better to stick that on the elemnt with .data() instead of using a global variable:
$(function () {
$("#submit_tab").click(function (e) {
var $cont = $('#submit_cont'),
animating = $cont.data('isAnimating');
if (animating) {
return;
} else {
$cont.data('isAnimating', 1);
$("#submit_cont").show("blind", 1000, function() { $cont.data('isAnimating', 0); });
}
});
});
Something like this (see documentation) :)
$("#submit_cont").show("blind", function(){
animating = 0;
});
You can add a $("#submit_cont").clearQueue(); after the animation finished :
$("#submit_tab").click(function (e) {
$("#submit_cont").show("blind", 1000, function() {
$("#submit_cont").clearQueue();
});
});
Updated JSFiddle
I found a different solution for this, which in my opinion looks cleaner:
var tab = $("submit_tag");
tab.on("click", function(){
var cont = $("submit_cont");
var animating = tab.queue("fx").length;
if(animating === 0){
cont.show("blind", {}, 1000);
}
});