by Dan DeMeyere - @dandemeyere
Sorry about the ugly title, I couldn't think of a better way to describe the post in fewer words. The long title would have been: "How to paginate through content with AJAX and still be able to share the current content piece that is viewed without the page refreshing". See why I shortened it? Onto the post.
AJAXing content in and out creates a nice experience for the user when done correctly. You can flip through posts or articles in a magazine-like fashion. If you're having trouble visualizing this, a good example is daily.thredup.com. The Daily Thred is a new project that a colleague of mine, Heidi, and myself created over the past month. Heidi was in charge of all of the front-end (design/layout/templates etc.) and I was responsible for the back-end implementation and JavaScript. Since we're a Ruby on Rails shop here at thredUP, I chose the Refinery CMS gem to handle the majority of the functionality. As for the JavaScript library of choice, we use jQuery. I love jQuery.
jQuery makes working with JavaScript fun. Seriously. A lot of people think of JavaScript and cringe, but I always look forward to the part of my project that involves JavaScript. Anyways...a big requirement (at least in my mind) of this project was being able to share the URL at any point in time on The Daily Thred and having that URL reference the current piece of content the user was viewing when they copied the URL. If you're reading this, you probably know that if you change the URL in the browser (i.e. thredup.com/how-it-works -> thredup.com/register) with window.location.href, the page refreshes automatically. Not very convienent for the problem we're trying to solve, but I'm sure there are security reasons for having the browser respond the way it does. The answer is not the window.location.href object in JavaScript. Close though, the answer is the window.location.hash object. Subtle yet important difference between the two.
The hash portion of the URL (http://thredup.com#hash_portion) is not visible to the controller. You can play around with the params variable in Rails all day or the $_REQUEST variable in PHP, but you'll never find the string 'hash_portion' anywhere. This is why we have to use JavaScript. I came to this conclusion after doing some brief reverse-engineering. I knew Facebook allowed you to browse pictures without the page refreshing while still being able to the share the URL of the specific photo you were viewing at all times. So I watched the URL while flipping through some photos and sure enough I saw that the URL was changing with each new picture, but only after hash portion of the URL. I figured this must be the key and I began Googling, which is a developer's best friend.
After reading a couple of posts on Stack Overflow, a developer's 2nd best friend, I quickly discovered a couple of plug-ins and libraries that already existed for this exact purpose. The only problem was they didn't seem easy to configure to our app's needs and they weren't lightweight. I wanted something fast, very fast. I also wanted the feature to fail gracefully in the event garbage was entered to the URL's hash parameter. So I decided to write my own implementation.
There are two parts to my code. The function definitions and the setup calls. As usual with most jQuery plug-ins, the setup calls need to be made when the DOM is ready. I also have an additional call that should be the first jQuery call made after the jQuery <script> includes. Here is the setup:
The paging.check_hash() call (which I'll show the code for in a second) checks the URL's hash string to see if the URL is actually referencing another piece of content. This function will intercept the current page from loading and swap out its contents for the hash string referenced content before the user ever notices. Here is the rest of the code:
As you can see the URL's hash string is updated upon a successful AJAX request. You'll also notice there's a function called refresh_social(). The purpose of this function is to find the Facebook Like and Twitter Tweet social plug-ins on the page and activate them. My controller returns a block of html to the AJAX method and therefore the JavaScript within that block will need to be called/refreshed once the DOM is visible to the new elements. Without those calls, not only would the Facebook/Twitter widgets not be unique to each content piece, they would never be clickable as the 3rd-party API calls need to be made first.
Hopefully that was helpful in your AJAX endeavour. As always, shoot me an email (contact info below) if you have any questions or see any flaws in my code. Also, if you're looking for a new job and you're interested in learning Ruby on Rails or are already a RoR developer, send me an email and I'll forward it along to the right people because thredUP has some openings on the dev team (and it's an awesome team to work for).
The next step for this plug-in will be adding forward/backward functionality with the hash event. Should be fun.
Dan DeMeyere
Ruby on Rails Developer & Front-end Engineer
dan@thredup.com