I apologize in advance if the title of this post isn't terribly clear. For a while now I've wanted to build a proof of concept JavaScript application that demonstrated automatically displaying new content. Think of your Facebook news feed as an example. In a trivial application, you could simply get your new content and prepend it to the content. However, I wanted to do something a bit more complex.
First - I wanted to gracefully display the new content via animation. So instead of simply "popping" in, you can see it slide in and push the older content down.
Secondly - and this is a bit of a pet peeve of mine, I wanted to make the application not shift the content when a) I'm mouseovered the text or when b) I've scrolled down a bit to focus in one one particular news item. Why? Well, for the first item, I can't tell you how many times I've been about to click something in TweetDeck when the app decides to scroll down content when new stuff is loaded. It's very annoying. As for the second item, I'll often find myself reading something when the feed decides to update. Again - annoying.
So let's start off with a first draft of the application. In this version, I'll run a scheduled process every few seconds or so, get new content (which will just be random), and simply prepend it to my div. Here's this version:
I'm assuming most of this will be pretty basic for my readers. I'm making use of Handlebars for my display. You can see the template generation first in the document ready block. I set an interval of 4 seconds for generating new content.
My checkData function handles getting the new data. I select a random number of items from 0 to 3. If we have any number over 0, I generate fake news items for each. (If you want to see my random text generators, just view source on support.js when I link to the demo.) Finally - we take each new item item and prepend it to a div. This is as about as simple as it gets.
For my next iteration, I decided to work on the animation. jQuery has a nice animation method called slideDown. Given hidden content, you can use this to slowly reveal the content over a duration. Here is the modified version for the updated display code:
This was pretty simple, but has a small bug in it. In any iteration of checkData, you may have multiple new items added to the stream. With the code above, each one has its own slide effect. When you have 2 items you get a "venetian blinds" type of effect. While this was wrong, I thought it was kinda cool too:
I figured that I could fix this by applying the hide/slideDown to a 'wrapper' for all of the new content in any given run of checkData. I modified the code to create a new div around my N news items and then applied the animation to that:
This worked better: Third Demo
Woot! Ok, so at this point, I thought I had it displaying adequately. Now for the hard part. I wanted to find a way to pause updating when you either mouseovered a news item or scrolled down some. I decided to first worry about the mouse events.
I pasted the entire template since this one has changes in multiple areas. First - note the mouseover/mouseout event handlers. When we mouseover, I update a global variable to indicate we should hide updates. When we mouseout, we flip that and run a new method, addToStream. Note that "html" is now a global variable.
If you scroll down to checkData, you can see now that we check the hiding status before we display anything. Because we have 2 places that need to display html (remember the mouseout event above), I abstracted it out into its own method. addToStream simply renders out the items and clears the global html variable.
To test this, visit the fourth demo with your console open. When a news item appears, mouse over and wait. When you see that items were added (in the console log), mouse out and they should show up.
Ok... still with me? For the final modification, I wanted to make it so that if you had scrolled down (in theory to view a news item), the main stream div would not update. This is rather easy if you listen for scroll event on the window object. However - I also wanted to make this "rule" have less priority then the mouseover rule. Since I only had one global variable (hiding), I needed another flag just for when I had mouseovered content. This code block has the relevant changes.
Looking at the mouse events, you can see the new variable being set, mouseOvered. Below them you can see the new handler for scroll events. I'm not terribly sure how smart this is, but it seems to work ok in my testing: Fifth Demo
So, what do you think?
Archived Comments
Brilliant. There's a lot of apps out there can could use this and then make viewing streamed data a lot less annoying (for the reasons you cited).
If it could control multiple streams and was available as a plugin, then that would be really useful.
A plugin would be cool. I could imagine something like
$("#stream").streamify()
to 'enable it', and then
$("#stream").streamify({add:arrayofcraphere});
And the plugin would handle knowing if it can show the crap or has to wait for it.
Agreed, but even better would be if you could just include the plugin and then do.
<div id="streamify_1" class="streamify"></div>
<div id="streamify_2" class="streamify"></div>
<div id="streamify_3" class="streamify"></div>
...
...
etc
Streamify is a great name for it, appeals to my english sense of humour.
I've not seen handlebars before. That along with this is something new to toy with. BTW, your handlebars link is broken: http://handlebarsjs.com/
Paul, check out my article here:
http://www.raymondcamden.co...
Fixing the link now - thanks.
Your mouseover/mouseout events are re-firing on every node in the #stream subtree (which will usually work but it's not really your intention). You should switch to mouseenter/mouseleave instead.
I thought about this a little more, and put together an example of what I call 'counterscrolling'. When the browser is scrolled, instead of pausing the prepend, simply prepend and scroll the window in the opposite direction so the viewport stays on the same part of the stream. If the viewport is at the top of the stream, use scrollDown like in your example.
I've created a Fiddle to show it in action: http://jsfiddle.net/fFsaG/2/
It's a slightly different user experience, but it should really solve your problem.
Updated fiddle: http://jsfiddle.net/fFsaG/5/
Damn it - updated again: http://jsfiddle.net/fFsaG/6/ (sorry, counldn't edit my comments)
Jens - thanks for all of this, especially the mouseenter/leave tips. I tried that and see how it works better.
Ray, How would you propose connecting to a cfc remotely within this same example?
You would just change my random generator to one using Ajax. I assume you know how to do that and how to call CFCs remotely?
I have done it it with autocompletes. It is just a matter of ironing down the jQuery syntax that I am still "green" on.
Ah, then you want to look at the API docs for jQuery for get.
http://api.jquery.com/jQuer...
Thanks Ray... This should get me going in the right direction.
Hello Raymond,
Thanks for sharing the process of thoughts about your concept. I am also annoyed when the Tweetdeck moves items down for new items and my click most of the times gets something I did not intend to click on.
However, I have a question about a concept I working on myself similar to yours but with an added twist :)
I get twitter news save them to a db and display them on a page with a set interval to update the page with news as they come available.
However, I also want to allow the user to scroll down the page and get older news. For that I set up a script to check $(window).scroll() and when at the bottom of the page and there are news left in the db, it pulls it and appends it to the page.
Now the tough piece that I am seeking an advice about. I want to put those two together plus your piece to stop the news from being updated if a mouse is over a item.
However, I am facing an unwanted effect. If a user scrolls half-way through the page the scroll event doesn't fire which is by design. However, when the updateNews() function that pulls the data on intervals brings in new data from the db, the scroll event detects something and fires. This behavior first makes the news scroll from user view, as well as, loading data the user did not ask for.
Added to that is the fact that the page position is sometimes resetting to the top.
Can you please give me some hints or ideas on how to tackle this?! I would like to also ask if you are okay if I adapt your code to pause the news stream if a user is hovering over it?!
Thank you,
Haz
I'm not quite sure I get you. It sounds like you maybe want to stop running updateNews() if scrolled down?
Thanks for the tip. It is actually helpful point to start with :).
Now I am done with the double loading but still have in the back of my mind the way things are done in TweetDeck.
I am working on making my demo view-able and will send you the link to see what I am talking about.
Thanks for this wonderful site and thoughts sharing spirit!
Regards,
Haz