Heads up! This blog post hasn't been updated in over 2 years. CodePen is an ever changing place, so if this post references features, you're probably better off checking the docs. Get in touch with support if you have further questions.
UPDATE: I have the complete technique documented in a post on CSS-Tricks called “Server Side Mustard Cut”. Probably the a better explanation of how this all works.
Have you ever clicked over to a Pen from a mobile device? Say, from an email or a third-party Twitter client? You were probably sent to the mobile version of our editor. But, it might have felt a bit slow and janky. Hopefully that feels better now.
Here’s our situation. We don’t want to use a different URL for a “mobile version” of anything on CodePen. We largely use responsive design to shift things around and change up functionality when needed. But sometimes, we don’t just need a little bit of different CSS, or some extra JavaScript here and there. For the mobile editor, we need a completely new set of HTML, CSS, and JavaScript to do what it does.
So how do you, at the same URL, load completely different sets of HTML, CSS, and JavaScript depending on information about the client (browser)? Because HTML is part of that equation, it kinda needs to be a server-side thing. This becomes kinda easy if we know information about the browser server-side. Yet, we’d rather not do user agent sniffing. Always a classic debate, but let’s not get into that. We’re not doing it.
One way it could not be server-side is if you load a really super lightweight page that runs a little JavaScript to learn about the browser, then script-load everything else you need (including HTML) after that. Unfortunately for us, that’s not the road we travelled from the beginning. That approach takes some major re-architecturing that we don’t have time for at the moment. I thought perhaps we could get around it if I just Ajaxed for the entire document, including scripts and styles, of the different types of editors and just slapped it onto the page. The problem there is that nothing loads in order when you do that and essentially everything breaks. You have to script-load stuff in order if you want to do this (essentially mustard-cutting) load-absolutely-everything conditionally approach.
So we went around road. We learn stuff about the browser through JavaScript when we can, then save that information in a cookie. The cookie can be read server side, so we can make choices server side about what to send. Pretty much perfect, except for that first page load where the cookie doesn’t exist. We decided loading the wrong type of page is too sucky of an experience, so if the cookie doesn’t exist, we’ll refresh the page once so it does exist the next time. And only do that if 1) The screen size is small, because the default is the desktop view and doesn’t need a refresh and 2) the browser supports cookies, so we don’t get into an infinite refresh scenario.
Cool, done, right? Well that’s the road we took, but we didn’t do a great job at it. The script to do this we were loading way down at the bottom of the page. This is a situation where you want to run this as absolutely quickly as possible. That way if it needs to refresh the page, it gets started on that immediately rather than loading a bunch of junk it doesn’t need first. I think we did that because we relied on a little bit of jQuery in the script and wanted to make sure that was available by the time we ran it. That was stupid. So we moved it to the top and removed the jQuery dependency. We also started calling a window.stop();
right before the reload (if we do it) so any in-progress requests for resources get stopped immediately. Not sure how much that helps but it seems like it would.
PHEW! That was a lot of words for such a relatively small change.
// if browser supports cookies
// AND we aren't currently using that cookie (as determined by class name on html element)
if (navigator.cookieEnabled && document.documentElement.classList.contains("cookie_used_false")) {
// set cookie, 3 days out
var date = new Date();
date.setTime(date.getTime() + (3 * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
document.cookie = "screen_width=" + screen.width + expires + "; path=/";
// under 700 is considered mobile in our world
if (screen.width < 700) {
window.stop();
location.reload(true);
}
}
Server side, we can just use an entirely different layout if we determine it’s best to show the mobile editor. And this script just cruises right by super fast and does nothing if that cookie is present. Update: even better! Since we can detect if the cookie is used server side, we don’t even load the script at all if the cookie has been used.