Hey so I'm quite a noob but I was wondering if the script below didn't fire too much? And if so, can someone help me optimise it?
<script>
jQuery(function() {
jQuery(window).scroll(function() {
if((jQuery(".main-content").height() - jQuery(window).scrollTop()) < 702) {
jQuery("h1.product-single__title, .product-single__title.h1").addClass('titleScroll');
jQuery("#ProductPhotoImg").addClass('imgScroll');
jQuery("div#option_total").addClass('optionScroll');
jQuery(".template-product .product-form__item--submit, .template-product .product-form__item--quantity").addClass('addScroll');
}
else {
jQuery("h1.product-single__title, .product-single__title.h1").removeClass('titleScroll');
jQuery("#ProductPhotoImg").removeClass('imgScroll');
jQuery("div#option_total").removeClass('optionScroll');
jQuery(".template-product .product-form__item--submit, .template-product .product-form__item--quantity").removeClass('addScroll');
}
});
});
</script>
Thanks
From https://dannyvankooten.com/delay-scroll-handlers-javascript/
var timer;
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
// actual callback
console.log( "Firing!" );
}, 100);
});
What you're looking for is called "throttling". By throttling a function, it only get's triggered x times per second, where you can define x. There are multiple ways of going about this and also a lot of libraries offer this functionality. You can find a nice read here on CSS-tricks which focusses on the lodash implementation.
Another great way to save performance is using requestAnimationFrame() (MDN). This way you don't have to listen to scroll events, but you can tell the browser to perform some script on the next repaint. You could use it like so:
function doThis(){
// javascript you'd like to trigger
window.requestAnimationFrame(doThis); // call again to create a 'loop'
}
window.requestAnimationFrame(doThis); // initial call to get the 'loop' started
Related
Testing this out and I'm trying to figure out how to stop a delay if i click another attribute. I'll post the site address to make this explanation a lot better, but basically when you press menu my nav appears with a delay, when I press Assignment 6 I want everything else to hide, which it does, but I see that because I have a delay when it hides and it's not done delaying it will continue to print out the rest of the elements even though they are supposed to be hidden. Also a disclaimer, I've gotten a lot of heat on this site before because I think people think I expect an answer. This is not the case, I love to learn and although the answer would be helpful and I would be able to de-engineer it and learn it, I would much rather have some guidance. So yeah, I'm not just looking for an answer if anyone thinks that's what I'm on here for (I come on here when I can't figure it out any other way).
site
my jQuery script:
$(document).ready(function () {
//$('ul').hide();
$('ul li').hide();
$('nav>li').hide();
$('nav>h1>').click(function (event) {
event.preventDefault();
$('nav>ul li:hidden').each(function(i) {
$('nav>li').show();
$('nav>h1').hide();
$(this).delay(i*600).fadeIn(200);
});
$('nav>ul li:visible').each(function(i) {
$('nav>h1').hide();
$(this).delay(i*600).fadeOut(200);
});
return false;
}); //closes a.btnDown
$('nav>li').click(function (event) {
$('nav>h1').show();
$('nav>li').hide();
$('ul li').hide();
return false;
}); //closes a.btnDown
}); //closes .ready
setTimeout is a useful mechanism to solve what you are after. It waits for (at least) the delay specified, and executes the callback function.
var elements = $('nav>ul li:hidden');
var timeoutId;
function doAnimation(index) {
timeoutId = window.setTimeout(function () {
if (index < elements.length) {
$(elements[index]).fadeIn(200);
doAnimation(++index);
}
}, 600);
}
The clue is to declare the timeoutId outside a recursive function, and assign it within the function. By doing it in a recursive fashion, you don't start the next timeout before the current timeout is finished, and it can be aborted at any time.
window.clearTimeout(timeoutId);
I've made a little fiddle that demonstrates the concept, but I haven't implemented a complete solution. Hope this helps you get further with your project.
http://jsfiddle.net/pyMpj/
You can replace your delays with setTimeout and clear them with clearTimeout
$('nav>ul li:hidden').each(function(i) {
$('nav>li').show();
$('nav>h1').hide();
var fadeTimeout = setTimeout(function () {
$(this).fadeIn(200);
}, i * 600);
});
$('nav>li').click(function (event) {
$('nav>h1').show();
$('nav>li').hide();
$('ul li').hide();
clearTimeout(fadeTimeout);
return false;
});
I have a couple of events that get fired depending on how far the user is on the page. Right now I'm using this
$(window).on({
scroll: function() {
trigger_scrolled();
}
});
I have been fiddling with the idea of check every X amount of milliseconds but I don't know how they compare.
Right now the app but it is very memory consuming. Is there a faster way to do this? or any other alternative?
You can limit the amount a function is being called by using a throttle/debouncing mechanism. Underscore.js has one, use a jQuery plugin, or write your own
Use JQuery scrollTop()
var allowed = true;
var timeoutID;
$(window).scroll(function () {
if (!allowed) return;
allowed = false;
if ($(document).scrollTop() > 1000) {
alert("Do stuff");
}
timeoutID = window.setTimeout(function(){allowed = true}, 3000);
});
See fiddle: http://jsfiddle.net/K6aRw/1/
Edit: added a time-out to make it check less oft.
I have a js file that contains my closure, this file is loaded before jQuery, let's say it can't be moved. How can I pass in or check for jQuery with a view to use it in the closure?
This is what I've got so far:
(function MyClosure() {
var interval = setInterval(function() {
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
function doJqueryStuff() {
// Some stuff with jQuery.
}
})();
It actually works, but is there a "better" way? I always think I'm doing something wrong whenever I use setInterval() for things like this, also the fact I am losing time in that 500ms.
You could wait and attach your execution to the window.onload event, assuming jQuery is loaded once the window is loaded...
window.onload = function() {
// do stuff with jQuery
};
Don't worry - while it does look hackish (at least to me and you) it isn't bad. Often times you need to wait until a complex object is initialized and you need to do the same thing. The best thing is to just ensure the order that your scripts load to solve any dependency issues - but as you requested let's assume the order can't be adjusted.
The only improvement I would suggest: adding an escape hatch to anonymous setInterval function. That way if jQuery never becomes available for some reason, the script can notify the user and stop checking.
var checkCount = 0;
var interval = setInterval(function() {
if (checkCount++ > 20) {
alert("jQuery could not be loaded - degrading user experience");
clearInterval(interval);
}
if (typeof jQuery !== 'undefined') {
doJqueryStuff();
clearInterval(interval);
}
}, 500);
Wait for the onload event on the script tag. In this case, the doJqueryStuff should be a global function.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" onload='doJqueryStuff()'></script>
Please, take a look at this code (I'm using Zepto http://zeptojs.com/ BTW)...
var timer = false;
$(window).bind('touchstart touchmove scroll', function (e) {
if (timer === false) {
timer = setInterval(function () {
$('footer').css('top', (this.pageYOffset + this.innerHeight - 40) + 'px');
console.log('Adjusted...');
}, 100);
}
}).bind('touchend', function () {
clearInterval(timer);
timer = false;
console.log('Cleaned it up...');
});
As you can see, I have a footer element that I'm trying to keep fixed on the bottom of the iPhone screen. I know that there are libraries that helps us make this quite easily like iScroll 4 http://cubiq.org/iscroll-4, but I was trying to see if I could make it simpler.
It turns out that the code above doesn't work properly. While I'm actually scrolling the page, for some reason setInterval doesn't execute but instead seems to pile up on the background to run every call at the same time.
At the end it doesn't do what I wanted it to do, which is to "animate" the footer and have it in place during scroll not only after. Does anyone has any idea on how such effect could be achieved on some similar manner?
Thanks!
When you pass a method to setInterval() (or any other function, for that matter), it will be invoked with a wrong this value. This problem is explained in detail in the JavaScript reference.
MDC docs
Inside your outer callback, this will be the DOM element you care about, but inside the setInterval callback, this will be window. Keep in mind that this is a keyword, not a variable, and that it is highly context sensitive.
The usual approach is to capture the value of this in a variable and then use that variable instead of this:
if(timer === false) {
var self = this; // "_that" is also a common name for the variable.
timer = setInterval(function () {
$('footer').css('top', (self.pageYOffset + self.innerHeight - 40) + 'px');
console.log('Adjusted...');
}, 100);
}
Similar issues apply to all callbacks in JavaScript, always make sure you know what this is and grab its value and build a closure over that value when it won't be what you want.
I need some help here..
Is it possible to cancel the chaining delay?
Mn.Base.TopBox.show = function(timedur){
$('#element').fadeIn().delay(timedur).fadeOut();
}
Mn.Base.TopBox.cancelFadeout = function(){
}
I read about queuing and tried some different approaches but I hadn't success...
$('#element').stop();
$('#element').queue('fx', []);
Thanks in advance,
Pedro
It isn't, .delay() doesn't play well with anything else since the timer keeps ticking and a .dequeue() is executed when it's up...regardless of if you cleared the queue and added a whole new one.
It's better to use setTimeout() directly if you intend to cancel, for example:
Mn.Base.TopBox.show = function(timedur){
$('#element').fadeIn(function() {
var elem = $(this);
$.data(this, 'timer', setTimeout(function() { elem.fadeOut(); }, timedur));
});
}
Mn.Base.TopBox.cancelFadeout = function(){
clearTimeout($('#element').stop().data('timer'));
}
What this does is set the timer and store it using $.data(), and when clering the animations, we're both calling .stop() to stop anything in process, and stopping that timer.
There's still the potential here for issues if you're firing this very rapidly, in which case you'd want to switch to storing an array of delays, and clear them all.