Hacker News new | past | comments | ask | show | jobs | submit login
Airbnb Releases Infinity.js: A Javascript Library for Smoother Scrolling (airbnb.github.com)
198 points by reissbaker on Aug 17, 2012 | hide | past | favorite | 95 comments



OT - I can't help but mentally shorten code snippets with underscore/whatever. For example, I see this -

for(var index = 0, length = listItems.length; index < length; index++) { listItems[index].remove(); }

and I think to myself -

_(listItems).invoke('remove')


That's awesome! Is that from underscore.js?


Yup. I love the philosophy underlying underscore. The functions are ridiculously simple, but so powerful to use once you grok it.


>The functions are ridiculously simple, but so powerful to use once you grok it.

As a Lisp hacker and current learner of Haskell, this makes my spleen rupture.

It's just functional programming 101, seriously.

That's all it is.


Just to let you know, while you're 100% right, I downvoted you for unnecessary snark.


Ack, didn't mean to offend you either. I like Lisp too (I'm learning Racket). I was just trying to explain how underscore.js makes me feel.


Maybe your approach is better for the advocacy of functional programming anyway, makes it less intimidating.


I'm sorry, but can we please stop with the 'grok' thing?


I apologize, I didn't realize it's offensive to you/others. Seemed like a perfectly cromulent word.


It might just be me. I just find it obnoxious.


Do you know the reference?


It's from Robert A. Heinlein scifi book "Stranger in a Strange Land".

In the novel native martians do exist and they are very smart. "Grok" it's the martian word for "water" and "to drink". Water is a scarce resource there, and, like here, very important for life.

Drinking is putting water inside you but also occurs that this water becomes part of you.

Then, in that society, drinking is used as the metaphor for understand something in a level that becomes part of you.

I'm a native spanish speaker and I don't know if that word sounds bad in english, but for me the metaphor is so powerful and beautiful that I like it very much.


I have been so conditioned by posting on HN to not reply with the banal, 'this', or, 'good one', that it almost seems wrong to just say, 'thank you for that post'.


Thanks ;)


FYI I knew the reference, but was wondering if the agitated OP did. You explained it better than I would have.

"To really understand something REALLY well".


Perhaps it's just been by a series of improbable coincidences that in the years I have read forums, IRC logs, books, and articles, I've only seen the term here on HN.


Could be a case of Baader-Meinhof [1], or maybe you haven't been socializing in right forums. The word "grok" has been in popular use on Usenet since the early 1990s at least, probably before, since it's from a 1970s Heinlein novel.

[1] http://wikibin.org/articles/baader-meinhof-phenomenon.html


"grok" thing? It's in the OED


Or, in LiveScript:

    map (.remove!),list-items


How does that work?


http://underscorejs.org/#invoke

In simple terms, it goes through the array, and calls the named function on every element. You could read it as - "For this array, invoke the 'remove' function on every element."


that's funny, I'm the other way around.. whenever I see syntactic sugar implemented via javascript, I unroll it in my head and consider it overhead, compared to sending a bunch of more bytes down the wire which hardly matter after gzip.


Underscore uses native methods for map/reduce/forEach when possible. For a modern browser the performance difference is comparable, or even faster for a map than an unrolled for loop:

http://jsperf.com/native-for-loop-vs-array-foreach-and-array...


TIL javascript functions are syntactic sugar.


And TIL stands for what, exactly? Sure you would not simply equate "syntactic sugar" with "syntactic sugar implemented in javascript".. so what are "TIL javascrip functions"? I tried searching, but no luck, woth none of the permutations. There are too many acronyms for TIL to just blindly guess.

My point basically was that

_(listItems).invoke('remove')

is actually short for

* [content of http://underscorejs.org/underscore-min.js] _(listItems).invoke('remove')*

and even if you removed all non-called parts from underscore, that's still a lot more complex than

for(var index = 0, length = listItems.length; index < length; index++) { listItems[index].remove(); }

it's just hidden, out of sight and out of mind.


TIL stands for Today I Learned.

They were attempting to use a meme acronym sarcastically -- an example of content-free commenting that works well for karma elsewhere but hopefully doesn't gain a foothold here.

On topic, I agree with your amplification of your point.


If you use memes on HN, you're going to have a bad time.

(They tend to be downvoted to oblivion, 'round these parts.)


Oh. Thanks, I get it now.

I guess while syntactic sugar is usually considered a functionality of the language itself, it surely is used in the way I meant it, too... random search result:

http://www.developerdrive.com/2012/05/an-introduction-to-und...

"In this part of the tutorial we’re going to take a look at some of the syntactic sugar that Underscore gives us to work with array-like collection."

And of course, even if I had been utterly wrong for using the term, my point would stand unmoved, but you know that :)


I can get where you're going with this, but as someone who uses underscore everyday I have to say in this particular case I don't agree with you.

Underscore as a production library is 4kb. The functions are terse, and its extremely easy to understand what they're doing - nothing is generally hidden (this example is pretty easy to understand, yet one of the most "complex" uses of underscore I've seen).

At the end of the day on a production javascript app of multiple thousand lines of JS, it likely actually saves me bytes over the wire from not having to write "for(var x = 0...." every time I want to iterate through something or filter an array or find if an array includes something etc (There's around 70 bytes removed in this single example).


Well, of course it matters how often you do it. I would not do it in loops, or often called event handlers, such as scrolling. If you do it once on page load, sure, it hardly matters.

But if you do it a lot in your code, that just means you can easily copy and paste it from a nearby place? At least that's what I often end up doing.


I've been using underscore for a few months and had no idea that you could call _ as a function until today. Pretty amazed that I missed this all along. Thanks for this!


I tested infinite scrolling on a large site about a year ago and the results were decidedly negative in almost every significant respect according to our A/B testing. Does anybody actually have evidence that infinite scrolling is a good thing?


HN likes to pretend that infinite scrolling has no utility, but it's simply not true. For sites where you want to quickly scan a lot of content, infinite scrolling is vastly superior to static pagination. For sites where you will typically consume every item in the list carefully and want to refer back to specific points in the list, pagination is vastly superior to infinite scrolling. They both have their benefits.


It's a good feature but it is often done so horribly it becomes not liked. Rule number one: If you use infinite scrolling instead of pages, don't remove pages. Especially on pages where content is somehow ordered (shops, lists, ..).


I agree it should be done well, but I don't agree you always need pages. For instance, when I use google images, I'm usually just looking for a few specific images in a sea of pictures and I don't care too much about keeping my place in that list. I care a lot more about being able to quickly scan through a very large grid of images, though, which infinite scrolling allows me to do.


The one caveat here is, if you're going to use infinite scrolling. Don't have links on the bottom of the page!

Sites like Facebook do this, and I have no idea why.

Want to "Create a page". Well there's a link at the bottom. But if you scroll to the bottom of the page, it's going to load more content and push the link down.

I just tried it, and the page reloaded 15 times before I gave up... never did get to the link.


On the right sidebar, under "Facebook (c) 2012" should be duplicates of all of the links at the bottom of the page. For the "Create a page" link, you have to click "More".

This is clearly unintuitive, but at least it exists.


Its probably there just in case scroll detection breaks. Some browsers - specifically BlackBerry browser (v4.6 to 5.0) - support AJAX lazy loading but don't support the scroll event.


Just a data point, I find lightbox (photo sharing site, not js lib) to be so incredibly addictive that I spent 1/2 hour on it. There's no way I would have spent more than 5 mins had I have to click on each page.


The most significant loss is the ability to keep your place. Google Reader is a good example. There's no way to bookmark how far back you've read so when I reload or close the tab I usually have give up and abandon unread items.

You could mitigate this but it would be useful to see what benefits people think infinite scrolling introduces and (if there are any) see if there is a solution that gives the same benefits with fewer costs.

Maybe some hybrid 'paged' infinite scrolling where you can keep going but clear pagebreaks are displayed which are mirrored in the browser history.


I have actually recently implemented something just like this where I use a combination of infinite scroll, pagination, ___pushState, and history to maintain your page reference. It uses Ember.js, so it even updates the pagination count for you as you scroll through the table. The infinite scroll is toggleable and you can decide to just stick with the "prev/next" pagination items as well.

Unfortunately, I had to custom roll this solution. I started on a plugin but making it universally usable for a number of use-cases is a bit time consuming.


The Danish dating site dating.dk uses a hybrid format with both page numbers and infinite scrolling (if I remember correctly). It's a site with a huge member base (considering the population of the country) so it seems to work well in the real world.


Pinterest seems like the best example. I don't have any numbers but explosive growth and fanatical users seem to be good circumstantial evidence for infinite scrolling. Prismatic also uses it and personally I love it on that site.


Personally I like infinite scrolling on a website like http://pr0gramm.com (warning: contains some explicit imagery, so NSFW)

I just check it out once every 2 or 3 days for a quick laugh and eventually I stumble upon some images I've already seen, which is when I close the site.


You've obviously never tried to surf porn in tumblr. It's much easier for accounts with infinite scrolling on vs those that use pagination. :)



Loading their demo linked from the word "on" froze my browser for about 20 seconds. Scrolling the page afterward was horrible, very jerky and laggy.

I have noscript installed, but all scripts on that page were allowed.


+1, count me unimpressed.

Off:

    A script on this page may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete.
    
    Script: https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:4
On:

    A script on this page may be busy, or it may have stopped responding. You can stop the script now, or you can continue to see if the script will complete.

    Script: http://airbnb.github.com/infinity/infinity.js:154
Firefox 14.0.1 without NoScript, Linux 3.4.0.


Same here, but I wouldn't expect much from a jQuery plugin that implements "infinite scroll". Like I need stuff to start loading by itself whenever I reach the bottom of the page.

Also, looking at their code, the first thing I laid eyes on was this statement:

  var infinity = window.infinity = {};
Which, in this context, would be the exact same as

  var infinity = {};


I believe they are doing that to override any functions/objects already bound to window.infinity from other plugins/scripts. Look at the infinity.noConflict() function at the bottom of the source, which seems to be inspired by the jQuery.noConflict().


It doesn't matter. Both snippets do the exact same thing, except the first has two attributions, the second just one.


Note to all developers planning on implementing infinite scrolling:

Don't have a footer. Example of this error: LinkedIn. Try to get to their ToS or Help Center from the logged in home page. Good luck.


It can, it just needs to be position as fixed.


I stand corrected. I personally like what Facebook now does (they used to have the same problem). They put the same information in a tile at the bottom of their right pane, which is fixed.


I tried both off and on demos, and all I got was a huge page loading slowly with scrolling causing flickering and stuttering. This is on Linux/Chrome. Maybe people shouldn't try to create large, image heavy pages in the first place, not to mention make them infinitely long.


Yeah, Mac/FireFox here and the same experience. both the on and off demos gave me a spinning beach ball for a minute while what seemed like the entirety of the content loaded. I thought that's what infinite scrolling was supposed to prevent?


UITableView was made for mobile devices not for infinite scrolling per se, but simply for large lists (such as address books) due to the memory limitations of iOS devices.

Those memory limitations are simply nonexistent for the web for almost all but the smallest number of cases (ie, infinite lists).

But lets pretend that this infinite scrolling is even a real problem (honestly it's probably not the best UI for large infinite lists of things on the web).

The current implementation clearly needs some work. It's trying to over-optimize for memory to the point of choking the scrolling.

This, however, can be fixed fairly trivially.

Allow the user to scroll anywhere (even past loaded content). If you can't eagerload elements to compensate for it, then lazyload after the user gets to that point. This gives users the illusion that the scrolling is truly smooth and responsive. The important thing is never allow scrolling to stop unless it's truly at a dead end and there is absolutely no more elements to load.

The above is how UITableView functions. Infinity.js is UITableView for loading elements, but does not function anything like UITableView from a user interaction standpoint.


...I don't think I understand the utility of this. Scrolling is already pretty smooth. (unless I'm wrong)


Author here. The utility is just to improve scroll smoothness (aka repaint times) of very-expensive-to-render pages, like infinite feeds with nice CSS effects. If you check out the demos linked to on the page, the demo with Infinity turned off should be noticeably choppier than the demo with it on, even once it's completely loaded and the browser's stopped reflowing. Probably depends on your machine/OS/browser combination -- but it'll keep loading more pugs as you scroll down, and eventually the version with Infinity off will grind just about anything to a halt.


For me, in chrome on win7, the on and off demos both appear to be about equally choppy. I doubt I could identify a difference in a blind test.


Strange. It's the other way around for me (brand new Win7 box, Chrome, 16gb ram, fast everything, waited for all images to load before scrolling).

When I move the scrollbar on the "on" version, it flashes white and constantly drops images in and out until I let go or stop in one place. Then it whites out again next time I scroll. Quite distracting actually, like viewing the bottom part of a long page while it's still loading images.

The "off" version scrolls as one would expect, no flashes, no missing or reflowing content. Just like you'd expect.

PgUp/PgDown and arrow keys don't seem any different between the two.


I did something almost exactly the same[1] for a design project at school a while back, where I explained why current infinite scrolling UX is not great and how something like this could help[2]. I'm glad someone made this into a library!

[1] http://newcoursys.herokuapp.com/courses/2012sp-cmpt-165-c1/a... [2] http://newcoursys.herokuapp.com/#marking-interface


By repaint times, do you mean re-layout times? ie. time required for CSS/HTML/JS to determine where to paint to?

The demo is not any smoother on my 27" Apple display driven by a late 2010 model MacBook air. However, even low res hardware accelerated video renders choppy at large window sizes (although perf gets better when VLC or QuickTime is true fullscreen; presumably the compositor is off)

Faster JavaScript is unlikely to be effective at enhancing buffer flip times, which (for me) dominate repaint times.


Both repaints and relayouts are improved, actually. The faster repaints are what improves scrolling performance (repaints are triggered every time you scroll, but relayouts are not) -- but because there are fewer elements in the DOM tree, relayouts are cheaper as well.

We experimented with using display:none instead of removing the elements; it increased scrolling performance just as much as actual element removal, but the relayouts when new content was appended got to be prohibitively expensive once a certain amount of content was loaded (literally pausing the browser for a noticeable amount of time), because the browser still had to traverse a giant tree of HTML entities during the relayout. So even if the buffer flip times are still too slow for you to get completely smooth scrolling in complex demos like this -- and that makes sense, given your large-screen setup -- you'll hopefully still benefit some from the faster, non-browser-freezing relayouts.


Both repaints and relayouts are improved, actually.

When actually is defined as for some on certain machines, judging from the number of directly contradictory observations in this thread.

The word "actually" ought to be reserved for concrete facts with zero counter examples.

The word "hopefully" in your closing sentence seems more apt.

That said, very much appreciate the sharing of the hypothesis and implementation. It's great to see ideas from other areas (video game terrain feature pop in) leveraged to improve end users' browsing experience.


The first paragraph is not tremendously helpful.

"UITableView for the web" … what does that mean? Why should a web dev know or care?

Make "demo" not "on" and "off" prominent. That's what I'm scanning the page for. Especially if you say "Infinity on and off" and it's the first time you use the word Infinity at all. Use Infinity or the symbol and then stick to it. At least introduce the name first.


It loads content as you scroll down the page.


As far as I can see, it also removes invisible "pages" of content(i.e. stuff you saw and you are about to see) from DOM to decrease the load on renderer


Hm, it doesn't degrade very well without javascript on (pagination would be a neat fallback). I know it's not generally worthwhile to cater to such a limited user population, but what about search engines? Should this only be used for content you don't need indexed, or you have indexed a different way?

Or, since I think Google uses javascript in its crawlers, I wonder how it handles it. Presumably it would just keep scrolling until the content ran out or it gave up. But does it ever come back later? I know with pagination it could pick up on the links it left off on, but couldn't really do that here, right?

edit: I guess this is really a question of infinite scrolling more than this particular implementation. Kudos to Dropbox for releasing a well-done version of the technique.


If this only works on jQuery objects, why not implement this as jQuery plugin?

  jQuery.fn.infinity = function (options) {...};
  $(el).infinity(options);
And so on.


I think it might work with other methods of selecting DOM elements. At least that's what the API suggests.


Maybe I'm not reading it right, but it says it depends on jQuery and accepts selectors or jQuery objects, not DOM elements.


OP: Maybe you should change the links [on]/[off] to [demo on]/ [demo off].


Thank you for using sane js code style.


If you try to move really fast with Infinity on, the screen turns completely white for a split second: http://airbnb.github.com/infinity/demo-on.html


True fact. That's just due to scrolling fast enough to get outside the buffer before the throttled scroll event fires -- if you want, you can turn the scroll throttle down to make sure that doesn't happen, at the expense of doing slightly more computation while scrolling. For Wish Lists, we optimize for people browsing through the feeds, which is generally not super fast. :)


Its much faster for me as well.

In order to calculate the size of each ListItem you add it to the dom briefly when its appended to the ListView. This seems to be a fast operation since you do it hundreds of times yet the page is loading quickly. Is there a trick there that is avoiding having to calculate a ton of layout, or is that just not a big concern compared to other work the browser is doing?

Thanks!


Just one trick, although I use it in both the on and off demos to keep everything fair: I'm adding elements in smaller batches of 70 rows per tick of execution (arbitrary number), and then using a setTimeout to wait a few milliseconds before adding more. The demo loads a tooon of elements upfront -- much more than an ordinary site would -- and Firefox would sometimes complain if I loaded 400 rows of pugs at a single time. Chrome, of course, had no complaints, and IE was surprisingly fine as well.


And from the comment above:

"...but because there are fewer elements in the DOM tree, relayouts are cheaper as well"

If I understand, this also applies? When I add the 400th (or whatever) ListItem it does trigger a layout when its added to the dom (to calculate its dimensions) but its much faster due to only having the currently viewable ListItems contained in the ListView?

Thanks again!


HN won't let me reply to your last comment -- nesting got too deep, maybe? -- but yup. Relayouts are less expensive as well.


yeah I experimented with NUM_BUFFER_PAGES and SCROLL_THROTTLE and I am sold :) thanks for building and releasing this!


No problem, I'm glad you enjoy it! :)


We (at our company) don't use pagination anywhere now. But we do have extensive search abilities. We have found, very few of our users just scroll. They use the search. But the "infinite" scrolling is there if they want it.


Cool! So basically it's a lazy load


Android 4.1.1 with or without Infinity: "The page has become unresponsive. Wait or kill?"


Both demos are the same for me. Chrome, Windows 7 64bit.


Awesome! This is going to help so much.


I think Lue Links was doing this for several years. I'm glad to finally have a library for it.


why it's based on jQuery?


Because re-inventing the wheel is soooo 20th century.


what if I don't use jQuery in my project?


Just for you, I went through their source, and these are all the jquery specific calls -

.height() .width() .offset() .remove() .append() .prepend() .prependTo() .find() .parent() .on() .off() .scrollTop() .outerHeight() .length

It seems to me like all of these would be covered by zepto. Or qwery. Or you could make your own adapter with Prototype. Or Mootools. Or Ext. Or YUI. Heck, you could probably whip up your own little library based on sizzle, or even just querySelectorAll. Then just fork infinity to use your lib, and you're good to go.

I hope you're not confused any more!

[I'm not affiliated with airbnb, just thought I'd help you out]


ok, sounds like a lot of boilerplate code to write, because "Its only dependency is on jQuery."


<script src="zepto.js"></script> <script> var jQuery = Zepto; </script> <script src="infinity.js"></script>

There.


off version actually worked better for me




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: