Grow your CSS skills. Land your dream job.

Slide In (as you scroll down) Boxes

Published by Chris Coyier

I was playing with my new Nexus 7 (I really wanted to own a real Android device) and I noticed a neat little effect in the Google+ app that comes with it. As you swipe down, new modules of content slide up into place. Video is best here:

We can do that! It's a pretty easy two-step process:

When the window scrolls, check if module is visible

jQuery has a :visible selector, but that isn't what we need here. We need to see if any part of the element is within the visual viewport. That is, an element may be technically visible, but located off-screen. We can figure out if an element is within the visual viewport through math. How wide and tall is the window, how far down is it scrolled, how wide and tall is the element, where is it positioned, etc. Fortunately that math is summed up super nicely by the visible plugin by Digital Fusion.

(function($) {

  /**
   * Copyright 2012, Digital Fusion
   * Licensed under the MIT license.
   * http://teamdf.com/jquery-plugins/license/
   *
   * @author Sam Sehnert
   * @desc A small plugin that checks whether elements are within
   *     the user visible viewport of a web browser.
   *     only accounts for vertical position, not horizontal.
   */

  $.fn.visible = function(partial) {
    
      var $t            = $(this),
          $w            = $(window),
          viewTop       = $w.scrollTop(),
          viewBottom    = viewTop + $w.height(),
          _top          = $t.offset().top,
          _bottom       = _top + $t.height(),
          compareTop    = partial === true ? _bottom : _top,
          compareBottom = partial === true ? _top : _bottom;
    
    return ((compareBottom <= viewBottom) && (compareTop >= viewTop));

  };
    
})(jQuery);

All that's left for us is using it when the window "scrolls" to add a class name.

$(window).scroll(function(event) {
  
  $(".module").each(function(i, el) {
    var el = $(el);
    if (el.visible(true)) {
      el.addClass("come-in"); 
    } 
  });
  
});

CSS will handle the slide-in:

.come-in {
  transform: translateY(150px);
  animation: come-in 0.8s ease forwards;
}
.come-in:nth-child(odd) {
  animation-duration: 0.6s; /* So they look staggered */
}

@keyframes come-in {
  to { transform: translateY(0); }
}

If elements are already visible, let them be

So we don't trigger the animation on the already visible modules on the very first swipe, we'll add a class name to remove that animation. And we can get a bit more efficient here too, "caching" selectors so we don't have to find them on each scroll event (which fires tons of times).

var win = $(window);
var allMods = $(".module");

// Already visible modules
allMods.each(function(i, el) {
  var el = $(el);
  if (el.visible(true)) {
    el.addClass("already-visible"); 
  } 
});

win.scroll(function(event) {
  
  allMods.each(function(i, el) {
    var el = $(el);
    if (el.visible(true)) {
      el.addClass("come-in"); 
    } 
  });
  
});

That'll do:

Check out this Pen!

And note, the CSS transition could be anything. They could slide in from the sides, they could use scaling and opacity and look like they were flying in. There could be color involved. Whatevs.

Comments

  1. Permalink to comment#

    Cool, but the slide effect only works the first time you scroll down…

    • Yep, that’s how it works in the G+ app. Like a visual indicator “this is new, you haven’t seen this yet.”

    • Check out this fork for a different effect that works going down and up.

    • I’m also sure there is an AJAX-y infinite scroll thing going on here. It appears that there is a lot more animation on my Nexus 10. It like comes from above and up… if that makes any sense. Like a 3D transition.

    • You could get it go to both ways by tying in the visible plugin with the toggleClass function. For instance. Just be warned that it’s a strange effect when you scroll back up – not intuitive at all.

      var mods = $(".module").addClass(function () {
          return $(this).visible(true) && "already-visible";
      });
      
      $(window).on("scroll", function () {
        mods.each(function () {
          $(this).toggleClass("come-in", $(this).visible(true));
        });
      });
      
    • Agree with Anja.

      The “this is new, you haven’t seen this yet.”… I think this is a very subjective interpretation.

      I personally see this behavior as a mere enhancement to the UI experience, that’s it.

      And if you’ve ever seen the iPhone/iPad app Vodio, it has a very similar effect when scrolling down, but it also has it when scrolling up.

      It honestly feels more “complete”.

      Go ahead, check out Vodio and see for yourselves.

    • Peter

      I’m trying to use something like this on a website I’m working on but I can’t get it to work. First I thought it was me, but I tried copying the whole working code from codepen, and it does all the animation instantly and not on scroll.
      What’s wrong here?

  2. Pretty nifty sure we will end seeing it on a ton of grid based sites now.

  3. Awesome Chris :D

  4. Dylan
    Permalink to comment#

    Glad to see how this was done, the simplest route is usually the correct one

  5. James Basoo
    Permalink to comment#

    Works great on desktop, but in Chrome on my HTC One X the box first appears in its final position before jumping down and doing the slide in animation to it’s final position. Does it do this on the Nexus 7 also?

    • Sichao
      Permalink to comment#

      Exactly the same problem with mobile safari here. Have you figured out what the problem is?

  6. Kyle Ouellette
    Permalink to comment#

    Would it be beneficial to remove the seen element from the cached array so that if you have a page with hundreds of elements, the array that you’re iterating over gets smaller and smaller as you go?

    var $modules = $('.module');
    $(window).scroll(function(event) {
      $modules.each(function(i, el) {
        var $el = $modules.eq(i);
        if ($el.visible(true)) {
          $modules
            .splice(i, 1);
          $el
            .addClass("come-in")
            .removeClass('module');
        } 
      });
    });
    
    • Very smart

    • Nicely done! A small and picky observation. It seems a bit confusing (to me) to call the removed classes “module.” If you’re working with other devs, I might class those elements something like “off-window” or to go with the “come-in” theme, “on-waitlist”. ^_^ :

      var $waitlist = $('.on-waitlist');    
      $(window).scroll(function(event) {
        $waitlist.each(function(i, el) {
          var $el = $waitlist.eq(i);
          if ($el.visible(true)) {
            $waitlist
              .splice(i, 1);
          $el
            .addClass("come-in")
            .removeClass('on-waitlist');
          } 
        });
      });
      
  7. Love this. I’ll definitely be using this sometime in the future!

  8. Heads up

    “how fall down is it scrolled,”

    should properly be

    “how far down is it scrolled,”

  9. Ha ha ,”I really wanted to own a real Android device” :D Nexus 7 is awesome indeed :)

  10. John
    Permalink to comment#

    sorry guys , but I can’t get the output mind to point out the error that i have made ?

                              <link rel="stylesheet" href="test.css">
    
    <script type="text/javascript" src="test.js"></script>
    <script type="text/javascript" src="http://raw.github.com/teamdf/jquery-visible/master/jquery.visible.min.js"></script>
    

    is it the correct way to use the visible plugin ?

    regards
    John

  11. Terry
    Permalink to comment#

    Hey, i get an error too…
    Check mine –> http://goo.gl/Niqiz

    Can you tell me what’s wrong with that?

  12. Desperado
    Permalink to comment#

    neat trick. Works in desktop Windows Chrome , but not in Firefox. OK – that’s not a big problem, it’s just an enhancement. But would it be possible to having it run in Firefox too?

    • qbin2001
      Permalink to comment#

      Works on my desktop Firefox without any problems.

    • try going through your apache server, assuming you have one. I find firefox doesn’t initialize js very well when working offline. Not sure why this is but that’s the ix.

  13. Simon Davies
    Permalink to comment#

    Nice bit of work, i bought a nexus 4 the other day as wanted a android phone (always been iphone) and i saw this and other things a few weeks ago and they have added soem nice little effects to keep you interest, most people wont notice (the wife). Great piece of snippet

  14. Scotty
    Permalink to comment#

    Nice, I always like this effect, glad to see a simple way to mimick it.

  15. Travis Miller
    Permalink to comment#

    It seems like it only works in the opposite direction in Safari

  16. Susan
    Permalink to comment#

    WOW! I just made a note yesterday, to figure out how to do this to show a long list of portfolio items for a client. Thank you!

  17. This jQuery plugin doesn’t seem to work for me on Safari Mobile or Chrome Mobile (on my iPhone5). Has anyone else been able to see this plugin work on a mobile device? I used it a few weeks ago on a pen of mine and noticed the same thing: http://codepen.io/dganoff/full/jtvay

  18. Stephen Cronin
    Permalink to comment#

    Don’t most mobile devices pause animations on scroll though?

  19. Dave
    Permalink to comment#

    This is pretty awesome :P Do you reckon it could be used with lazy loading a gallery or WordPress posts (for example)?

  20. Casey Dennison
    Permalink to comment#

    That is pretty nice! I’m going to implement this on my next grid based design.

    • Simon Davies
      Permalink to comment#

      Just added the google+ app to my ipad, and it’s go the same effect but even better, they fly in. Awesome

  21. I’m going to implement this on my next grid based design.

  22. Here is my fork. Now animation works in both directions.

    • Permalink to comment#

      Yo, Pawel, this is really great job!!! It really works both ways!!!

    • This is EXACTLY how I think this effect should work.

    • James
      Permalink to comment#

      Meh. Wayyy too much animation going on here. I much prefer the limited animation on Google+ that’s only present for new content.

    • Grsmto

      Same here. Google don’t do things to “make cool animations, lol its fun!”. This is a feature to help user to know when the new content begin.
      This is EXACTLY how I think this effect should NOT work.

  23. Permalink to comment#

    This really looks great, and it is instant inspiration. Only one potential downfall – as i am finishing site that slightly resembles this, but only with four squares, (temp location Mambo Stars temporary) and you can’t even imagine how terrifying it looks in IE less then 10. Conditional stylesheet must be done from scratch, and it is actually a totally new site, this one I don’t even want to imagine.
    Chris, IE7 is always a hair-pulling experience :)

  24. Permalink to comment#

    Very clever, and nice! You don’t need to see the animation once you’ve seen it once already. It’s perfect like this

  25. This animation is pretty for sure. I really like it but I wonder if it’s useful. You know you didn’t saw what’s hidden even without this animation. Doesn’t make sense to me.

  26. Kris
    Permalink to comment#

    I am so lost why this isn’t working for me, even when I try to make the same exact thing that’s on Codepen.

  27. LEDfan
    Permalink to comment#

    Nice stuff! I had some trouble with using the code. (Probably the same problem as Kris and other persons above have)

    The solution:

    The codepen uses SCSS, I think it isn’t nessecary
    You have enabled the ‘prefix free’ option in codepen, this means codepen will add the neccescary prefixes. When you copy the code to your own server, you will need to add the prefixes.

    Here is a codepen that works in Chromium and Firefox

  28. Nice effect, really thanks!

  29. Permalink to comment#

    Awesome .. got it working on desktops but not in mobile? Any way of making this work for mobile?

    Thanks :)

  30. It’s ok when you scroll slowly, but how does it looks when fast scrolling, especially on smartphones.
    Animation are locked when scrolling on iPhone too.

  31. Paul
    Permalink to comment#

    This is great, love the implementation! For more advanced ‘is it visible’, I suggest you check out jQuery.fracs.

  32. Permalink to comment#

    Working also with Zepto!

  33. Hugo
    Permalink to comment#

    Just thought I’d mention that this doesn’t work with Canary as it doesn’t allow scripts with the wrong MIME type anymore.

  34. hugo
    Permalink to comment#

    Sorry I meant the CodePen example doesn’t work, not the method.

  35. On Chrome when the class .come-in is applied the element appears in its final position for an instant before being translated down 150px. It seems chrome draws the <div> on-screen before translating it.This is rather visually jarring and slightly ruins the effect. To fix this the element should be invisible by default:

    .come-in {
      visibility: hidden;
    }
    

    And then after the js has applied the class .come-in it can undo the visibility attribute with a line of jQuery:

    allMods.each(function(i, el) {
      var el = $(el);
      if (el.visible(true)) {
        el.addClass("come-in");          // Not visible
        el.css('visibility', 'visible'); // Visible
      }
    });
    

    The visibility detection plugin ignores the css visibility attribute so this shouldn’t interfere with that, and if the user has javascript disabled then .come-in won’t ever be applied and so no content will ever be hidden!

    • Oh actually, visibility: hidden; should be applied to .module instead. Got my classes mixed up!

      If you want to ensure compatibility with non-js users you should apply visibility: hidden; using javascript rather than css.

      allMods.each(function(i, el) {
        var el = $(el);
        if (el.visible(true)) {
          el.addClass("already-visible");
        }
        else {
        /*  Like this!  */
        el.css('visibility', 'hidden');
        }
      });
      

      Combined with the js in my above comment this should work as promised! :P

  36. Salman
    Permalink to comment#

    I wanted to do this via ajax, such that when user scroll down it automatically fetches result in my case Images and display it with the same css3 effect. Is there any way ?

  37. Ronnos
    Permalink to comment#

    Thanks for the script. It’s working! I now only have one question. Is it possible to trigger the event a bit later (give it an offset of a certain pixels / percentage of the screen height). So the event is triggered when the element is scrolled at a certain “view height”?

    Thanks for your effort

  38. Mpa4Hu
    Permalink to comment#

    Is not this a little bit slow?

    I don’t know why but
    if (el.visible(true)) {
    el.addClass("come-in");
    }

    this part triggers my recalculate style (chrome timeline), even though I don’t even have styled my come-in class. and when I scroll enough, like more then 20 elements (big and havy) its just terrible experiance.

  39. Asif
    Permalink to comment#

    This script doesn’t work. strange. I compiled that SCSS to CSS, Included jQuery, copied exact same JS and HTML on plain text file, made a simple demo, but doesn’t work. anyone have any clue?

    • Michael
      Permalink to comment#

      Same issue here. When I do get it working it only works if you reload the bottom of the page and scroll up.

    • Matevž

      I have been struggling with that script to work for few hours until i removed body height:100%; now it is working perfectly.

  40. Zohaib Tariq
    Permalink to comment#

    what is the name of this new effect i have seen it on many websites

  41. chidhu
    Permalink to comment#

    its not working…pls help me

  42. Gavin
    Permalink to comment#

    Doing a browserstack test, and in older versions of IE it doesn’t work.. that’s fine but the fall back has one row of boxes with a large space at the top.

    Any ideas on how to get it to fall back a little more gracefully
    Thanks

  43. Hello Chris,
    I want to try this effect but facing problem.
    I tried it by copying all these codes but nothing happens.
    What should i do to run this effect.

  44. Kimomat
    Permalink to comment#

    Awesome!
    I have one additional optimization: on Resize:

    var $waitlist = $('.on-waitlist');    
    $(window).on("scroll resize", function(event) {
      $waitlist.each(function(i, el) {
        var $el = $waitlist.eq(i);
        if ($el.visible(true)) {
          $waitlist
            .splice(i, 1);
        $el
          .addClass("come-in")
          .removeClass('on-waitlist');
        } 
      });
    });
    
This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".