0

I'm having troubles getting this code to execute in a timely manner in Firefox. It seems to work just fine in Chrome.

JSFiddle here: http://jsfiddle.net/EXDhb/

Real live example page I'm working with here: http://mindevo.com/tests/tacos.html

I'm not sure if I'm leaving something out. I kind of hacked this together from reading a bunch of page-scroll scripts other people have put together. Not sure if this is even the best way for me to do what I'm trying to accomplish (which is to darken the next area until it's somewhat revealed. (I used halfway for this).

Here's my javascript:

$(document).ready(function(){
 $(window).scroll(function(){
  $('.dark').each(function(i){

   var half_object = $(this).position().top + ($(this).outerHeight()/2);
   var bottom_window = $(window).scrollTop() + $(window).height();
   var bottom_object = $(this).position().top + $(this).outerHeight();

   if(bottom_window > half_object){
    $(this).animate({'opacity':'1'},200);
   }
   else if(bottom_object > $(window).scrollTop()) {
    $(this).animate({'opacity':'.5'},200);
   }
  });
 });
});

Is there a better way to do this? I tried adding/removing css classes but it invoked some crazy Chrome bug I was not pleased about.

Why does it work so slowly in Firefox?

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
ChaBuku Bakke
  • 685
  • 1
  • 7
  • 24
  • I thought codereview was exclusively for working code? I don't see it as working presently... – ChaBuku Bakke Jun 16 '14 at 16:17
  • I'm not sure what you mean by slow, it seems to be working fine for me in FF30. – Igor Jun 16 '14 at 16:18
  • Rule of thumb for event handlers that fire rapidly like scroll ones: *don't* execute a DOM query inside of it if you can help it! DOM queries are expensive operations to perform. Cache your `$('.dark')` query in a variable outside of the scroll handler function. – ajp15243 Jun 16 '14 at 16:24
  • ajp15243: Thanks, I'm still pretty new to js so I'll explore that more right now. Igor: does it not take a full second or so for the "game3" and "game4" divs to go to full opacity after "game2" div has fired? I'm trying to make it happen immediately when it comes halfway into view. Also... why the downvote on this question? Do people new to something always get downvotes just because it's not an expert question? Frustrating. – ChaBuku Bakke Jun 16 '14 at 16:30
  • +1 for providing a JSFiddle :) – iCollect.it Ltd Jun 16 '14 at 16:31
  • I feel like JSFiddle and a real example are the best ways to communicate what I'm working with. Is there a better way to present my question so it doesn't get downvoted? I'm trying to maintain standard practices so I'm not downvoted to oblivion. – ChaBuku Bakke Jun 16 '14 at 16:35
  • Just try and explain the problem clearly and objectively (if possible). State facts. What it does wrong, what it does right, what you expect it to do etc. The problem this time was asking for *performance improvements* (not appropriate for this site) when the actual problem was *delayed animations* :) – iCollect.it Ltd Jun 16 '14 at 16:38
  • Ah. So my lack of understanding from the get-go got me a downvote nonetheless. Thanks for updating question to be more accurate. – ChaBuku Bakke Jun 16 '14 at 16:41

3 Answers3

2

Start by not having 6 separate jQuery $(this) operations and multiple $(window)! Use temp variables whenever you can to avoid requerying.

JSFIddle: http://jsfiddle.net/TrueBlueAussie/EXDhb/9/

$(document).ready(function () {
    // window never changes
    var $window = $(window);
    $window.scroll(function () {
        // Window height may have changed between scrolls
        var windowHeight = $window.height();
        var scrollTop = $window.scrollTop();
        $('.dark').each(function (i) {
            var $this = $(this);
            var top = $this.position().top;
            var outerH = $this.outerHeight();
            var half_object = top + (outerH / 2);
            var bottom_window = scrollTop + windowHeight;
            var bottom_object = top + outerH;

            console.log(half_object);

            if (bottom_window > half_object) {
                $this.stop().animate({
                    'opacity': '1'
                }, 200);
            } else if (bottom_object > scrollTop) {
                $this.stop().animate({
                    'opacity': '.5'
                }, 200);
            }
        });
    });
});

And so on until you do not do anything twice that has an overhead that you do not need to have.

Update: Stop previous animations

The pause was not caused by the speed of the code above, but by not stopping multiple animations. The problem is that scroll fires frequently, so without .stop() animations get queued up and fire one after the other. This made it look much slower that it actually was.

Further optimizations might involve only processing elements that are actually onscreen, but that is pretty pointless given the apparent speed now.

iCollect.it Ltd
  • 92,391
  • 25
  • 181
  • 202
  • Thanks, this seemed to speed it up a touch. It still seems to have quite a bit of pause which I'm not sure why. – ChaBuku Bakke Jun 16 '14 at 16:28
  • @ChaBuku: The pause was caused by not stopping multiple animations (fixed in latest version). See above. – iCollect.it Ltd Jun 16 '14 at 16:29
  • Thanks! This works great. Also learned a little from your answer. – ChaBuku Bakke Jun 16 '14 at 16:38
  • @Fresheyeball: Interesting link. Will have to try that too. Not really needed in this case as it just needed to flush the animation queue. – iCollect.it Ltd Jun 16 '14 at 17:01
  • @TrueBlueAussie it is beneficial in your case because the scroll event will fire many times per second, and this code will execute many times per second, where users will only benefit from once per frame (16ms). With `throttle` you wont waste any cycles. – Fresheyeball Jun 16 '14 at 17:54
1

You can cache your variables, which should help slightly:

$(document).ready(function(){
    var $window = $(window);

    $window.scroll( function(){
        $('.dark').each(function(i){
            var $this = $(this);
            var outerHeight = $this.outerHeight();
            var positionTop = $this.position().top;
            var half_object = positionTop + (outerHeight/2);
            var bottom_window = window.scrollTop() + window.height();
            var bottom_object = positionTop + outerHeight;

            if(bottom_window > half_object){
                $this.animate({'opacity':'1'}, 200);
            } else if(bottom_object > window.scrollTop()) {
                $this.animate({'opacity':'.5'}, 200);
            }
        });
    });
});
Igor
  • 33,276
  • 14
  • 79
  • 112
  • 1
    The real problem was the animations were repeatedly appended. That had a much greater impact than the temp-var code changes we both made. – iCollect.it Ltd Jun 16 '14 at 16:40
1

I realize there is already an accepted answer, but many times it is useful to do something only after the user has stopped scrolling, and not each time the "scroll" event fires. This event can can fire upwards of 50 times per second, leaving you with ~20ms to do what you need to do. This other StackOverflow question shows you how to do something only after scrolling has stopped. As @TrueBlueAussie mentioned in his answer, you would still want to stop any animations that were currently running.

Community
  • 1
  • 1
Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92
  • Thanks for this reference. It does seem like it takes a lot to calculate on this on the fly. I have to wonder if I'm going about the whole problem wrong and trying to find a different way to make it come in to view. This will be the first time I've made something like this from scratch(ish). – ChaBuku Bakke Jun 16 '14 at 17:04