I have this code for a website I'm working on. It uses window.scroll
events to parallax some items. It's running a bit slow on average machines. Is there any way I could improve it to make it run faster?
$(function() {
// Tell the DOM that JS is enabled
$('html').removeClass('no-js');
// Navigation Waypoints
$('.waypoint').waypoint(function(event, d) {
var a = $(this);
if (d === "up") a = a.prev();
if (!a.length) a = a.end();
$('.active').removeClass('active');
a.addClass('active');
$('a[href=#'+a.attr('id')+']').addClass('active');
}, {offset: '40%'});
// Parallax Effects
$(window).scroll(function() {
var s = $(window).scrollTop(),
b = ($('body').height()/100)*20;
$('.container').each(function () {
$(this).css('top', Math.round((($(this).closest('.waypoint').offset().top-s)+b)/$(this).attr('data-speed')));
});
$('#home hgroup').css('bottom', '-'+Math.round(s/6)+'px');
});
// FAQs
$(document).on('click', '.faqs a', function () {
$('.faqs .open').slideToggle().removeClass('open');
$(this).siblings().slideToggle('fast').addClass('open');
return false;
});
// Kinetics
$('.counter').kinetic({
y: false
});
// Smooth Scroll
$(document).on('click', 'nav a', function() {
var t = $($(this).attr('href')),
o = t.offset().top,
b = ($('body').height()/100)*10;
if($(this).attr('href') === "#home") {
$('html, body').animate({scrollTop: o}, 'slow');
} else {
$('html, body').animate({scrollTop: o+b}, 'slow');
}
return false;
});
// Set Margin
function setMargin () {
var t = $('#about'),
o = t.offset().top,
c = $('.container'),
b = ($('body').height()/100)*20,
m = Math.round(parseInt(t.css('margin-top'))),
a = Math.round((((o/2)+b)+m)/2);
c.css('top', a);
}
// Calculate Padding
function calcPadding() {
var p = Math.round(($(window).width()-700)/2);
$('.pr').css('padding-right', p);
setWidth();
}
// Calculate Width
function setWidth () {
$('.draggable').each(function () {
var w = 0;
$(this).children().each(function () {
w = w + $(this).outerWidth() + parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
});
$(this).css('width', w);
});
}
// Window Resize
$(window).on('resize', calcPadding);
// Initialize Functions
calcPadding();
setMargin();
});
2 Answers 2
I don't know what the "waypoint" plugin is, but you might be able to optimize your scroll
event handler by caching the jQuery objects, data-speed values, and the results of the .closest('.waypoint')
calls:
// Parallax Effects
$(window).scroll(
(function() {
// cache the queries
var $body = $(document.body),
$containers = $('.container'),
$hgroup = $('#home hgroup'),
speeds = [],
waypoints = [];
// save the values for data-speed attributes
// and the waypoints / container relationships
// based on the index in $containers
$containers.each( function(idx) {
var $this = $(this);
// if the waypoints never change, you could just put
// the offset().top value in the waypoints array
waypoints[idx] = $this.closest('.waypoint');
speeds[idx] = $this.data('speed');
});
return function() {
var s = window.scrollY,
diff = ($body.height() / 100 * 20) - s;
// you don't need the Math.round(...)
$containers.each(function(idx) {
$(this).css('top', (waypoints[idx].offset().top + diff ) / speeds[idx]);
});
$hgroup.css('bottom', s / -6);
};
})()
);
If the results of waypoints[idx].offset().top
don't change, you could store those values instead of the result of the .closests('.waypoint')
in the waypoints
array.
Lastly, I don't think you need the 'px' Math.round()
when setting the CSS properties with jQuery.
I haven't tested this as you didn't provide a link to a live page or a jsFiddle, but in theory, it should work.
-
\$\begingroup\$ hey man thanks for the response. the staging site is up at: staging.lightenink.com I'll have a go at implementing your advice tonight! \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 12:44:09 +00:00Commented Dec 18, 2012 at 12:44
-
\$\begingroup\$ I just implemented your code and result wise is identical, I will try it on a slow machine in a bit to see if the performance has increased. Thanks so much for your help. Will try and get my head around what you did! \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 12:50:51 +00:00Commented Dec 18, 2012 at 12:50
-
\$\begingroup\$ This code seems to perform pretty well on slowish machines. Thanks so much for all your help! If you have any other advice on how I can improve the way I write my code it would be greatly appreciated! Thanks guys! \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 13:25:11 +00:00Commented Dec 18, 2012 at 13:25
The jQuery documentation offers the best advice:
Event performance
In most cases, an event such as
click
occurs infrequently and performance is not a significant concern. However, high frequency events such asmousemove
orscroll
can fire dozens of times per second, and in those cases it becomes more important to use events judiciously. Performance can be increased by reducing the amount of work done in the handler itself, caching information needed by the handler rather than recalculating it, or by rate-limiting the number of actual page updates usingsetTimeout
.
Obviously it would be ideal if you could move the calculations outside the event handler to reach the desired performance, but that may not be possible in this case. So to expand on the rate-limiting approach, there are 2 ways you could go about it:
Call event handler after the user has finished scrolling (hasn't scrolled for some set amount of time):
var timer = null; $(window).scroll(function () { if (timer) { clearTimeout(timer); } timer = setTimeout(function() { timer = null; // your event handling logic here }, 50); });
Call event handler immediately after scrolling, with a minimum delay between scroll events:
var justExecuted = false; $(window).scroll(function() { if(justExecuted) { return; } // your event handling logic here justExecuted = true; setTimeout(function() { justExecuted = false; }, 50); });
-
\$\begingroup\$ Hey man, thanks for the response, unfortunately I am updating the css top position of an element using the distance scrolled from the top as the user scrolls so I can't fire the function after the user has scrolled. \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 12:45:51 +00:00Commented Dec 18, 2012 at 12:45
-
\$\begingroup\$ I don't know if there is a better way of parallaxing my elements tho. \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 12:47:11 +00:00Commented Dec 18, 2012 at 12:47
-
\$\begingroup\$ @SimonSturgess: My recommendation would be the second approach I listed, where you execute your event handling logic immediately after scrolling, with a minimum delay between each scroll event. You only need to try this if caching the values (as in tiffon's answer) isn't fast enough. \$\endgroup\$seand– seand2012年12月18日 13:12:51 +00:00Commented Dec 18, 2012 at 13:12
-
\$\begingroup\$ I've tested tiffons code on a slowish machine and it seems to perform pretty well. Thanks so much for your responses. I'm an intermediate jquery user, just need to work improving the quality and performance of the code I write. \$\endgroup\$Simon Sturgess– Simon Sturgess2012年12月18日 13:24:15 +00:00Commented Dec 18, 2012 at 13:24
Explore related questions
See similar questions with these tags.
i
infor
cycles, I suggest you avoid one-letter variables.var a = Math.round((((o/2)+b)+m)/2)
is indeed compact, but not very nice to read. \$\endgroup\$