I have been fascinated with Facebook’s new gallery light box since it launched. The details of it’s implementation confused me for a while, and I knew I had to find the time to re-create it. After putting it off for far too long, last week I realized (with some amount of shock and awe) how it was being done. So I spent some time coding it up and thought I’d share.
First, a demo. If you scroll halfway down the page you’ll find a button to click that will reveal the light box. There is a close button on the top right corner of the light box. The demo can be found here: Facebook Gallery Lightbox
As you may have noticed, there are several cool things about the way this light box imnplementation works:
- When the light box is up, the window’s scrollbar represents the gallery height, not the height of the document.
- Your scroll position on the content is maintained (ie. it doesn’t move to the top of the document), and when you close the light box, you are right where you left off.
- The URL changes to reflect the new page you are on without refreshing the document.
Let’s go through the code, and we’ll tackle these each one by one.
The CSS and HTML
As you can see, the HTML for this demo is quite simple (made simpler by the fact that there isn’t any content :)):
The first element of note is the “canvas” element. This is an element I’ve had kicking around for a while for use in my CSS Sticky Footer (in this case you can ignore the “footer” element stuff). The element extends to the full width and height of the window, regardless of whether the body of the document is shorter than the window height or longer. This is particularly useful for light boxes, and as you can see here, there is a “screen” element, which is simply an absolutely positioned element inside “canvas” that takes up the entire width and height of the window as well. The CSS for the screen element is below (note: we are using a black color with a 30% opacity using a CSS RGBA color):
We also have the “gallery-wrap” and “gallery” divs, the CSS for which is below:
We use the “gallery-wrap” element here to create a horizontally centered div (margin: 0 auto) of the width 960 pixels (you can adjust this as necessary). Inside of that is the “gallery” element, which is floated left. Even though we’ve assigned it a min-height here to demonstrate the window scrollbars, because it is floated left, it will grow vertically to fit any content we put inside of it. You can add your photos, navigation, comments, menus, etc.
I have to applaud whoever came up with this since it’s pretty out-of-the-box. Basically, when the user opens the light box, the entire content of the HTML body is shifted upward and conversely, the height of the content is set to the height of the window. This means that the view is set to the section of the page you were looking at. Then, the light box is displayed, allowing the document’s height to fluctuate again with the height of the light box.
The first line (the old_top variable) is a record for us to keep. This variable holds the current offset from the top of the document (the current window scroll position); when the light box is opened, the “top” property of the content is set to be negative the current offset. The height of the content is also locked to the height of the window and its overflow property is set to hidden so that the content does not cause the window to scroll vertically. Finally, we display the screen and the gallery-wrap elements.
As I noted, the URL of the browser window changes when the light box is open. This happens on line 5: we use HTML5’s window.history.replaceState function to change the location of the window without redirecting the user or refreshing the screen. This makes the URL shareable.
To close the gallery, we basically do the opposite:
As you can see, we replace the current history item with our old location and hide the screen and gallery-wrap elements. Then, we allow the content to overflow, set the height back to auto to allow it to expand to fit it’s content, and set the top to zero to put the top of the content back at the top of the document body. Finally, we set the user’s current scroll location back to what it was using our storage variable.
Conclusions and Enhancements
I picked this apart because I was uber curious (and a little in awe) as to how it worked; that said, I’m sure it’s not perfect. In fact, I’ve already thought of several improvements that could be made (I will post a follow up if I can find the time to make them):
- Not all browsers will support the HTML replaceState method. This should likely be wrapped in a if(typeof window.history.replaceState == ‘function’) and code should be added to fall back to a hash-bang URL change or a simple redirect.
- This would pair extremely well with an infinite scroll like Facebook uses. This obviously requires a session’s scroll state to be saved and/or cached and you would have to return the user to where they once were. There was an excellent tutorial about this recently on Hacker News but I can’t find it for the life of me.
- Content for the gallery could/should be loaded in via AJAX. This could also be cached in the browser, etc., etc.
I am itching for a place to put this into practice; I’m sure my next project or site will find a way to incorporate it. Again, whoever came up with this deserves a beer/pat on the back as the outside-of-the-box thinking here actually shocked me when I realized how it worked.
If you have any questions or suggestions for improvement, please let me know in the comments below!