Jokel Embed Development

Two months later, I’m finally writing the blog post I promised in my original blog post showing off Jokel embeds Part of the reason it took me so long to write this post was that it was so remarkably easy to do that I had a hard time outlining a substantial post. Even if the actual development was simple, figuring out the implementation details took us about 30 minutes and there were a few gotchas that popped up after, so those are outlined below as well.

The idea, as many of our inspirations for Jokels, came from Github: we really like to use Github’s “Gist” snippets on our blog. It’s really slick how easy it is to embed code samples with syntax highlighting, update the snippets without touching the post and to comment on or even fork code snippets. So we thought it’d be fantastic if we could offer the ability to embed Jokels. It would be a slick way to embed a joke in a blog post or your website, drive traffic to the site and as people add alternate punchlines those could show up as well, so we would get some of the cool dynamic update elements of Github’s embeds.

So, continuing our history of liberally stealing from Github, we looked at Github’s embed tag, and then inspected the Javascript file that it was referencing. It turned out to be remarkably simple. It used document.write to embed link tags to stylesheet and javascript files it needed, then wrote an escaped HTML / javascript snippet containing all of the Gist in a single line. It seemed clear that Github was probably just using <%= escape_javascript %>, so it was relatively simple to do the same:

The only tweaks we had to do were to change the existing Joke partial to accept a new “min” parameter which turned off everything except for the simple joke functions (no sharing, voting, new random joke, etc) and to move the necessary CSS and Javascript out into independent files so that we weren’t sending irrelevant code in our embeds – we want our embeds to function as quickly as possible so people will use them!

Of course, no commit is ever perfect (at least none of mine ever are) and a couple of issues popped up along the way, some of which surprised me, others of which I just forgot about during the process:

  • Style for the page in which the embed was being placed would break our layout, if we didn’t have fine-grained CSS selectors for the given elements. The example we found was that a page had a margin specified for all spans, but we wanted out spans to have a 0 margin, which was applied at a general class level, so the more specific selector one out. We tried to combat this with more specific selectors for important layout attributes, as well as “!important” flags for attributes.
  • We use mobile-fu on all our requests to catch incoming mobile devices and route them to our mobile site – but with embeds we clearly didn’t want this to happen: we have no control over whether the embed site is a mobile version or not, so we should always just display the normal version – so we had to add exclude clauses for these request handlers. It was an easy fix, but it caused several airbrake errors when someone tried to view our blog on a mobile device.

All-in-all, adding embeds was a remarkably easy thing to do in Ruby on Rails by leveraging the power of existing code and partials. It’s a great example of a low-cost but high-value and high-visibility feature that most sites could use. Just to show it off one more time, and to give my favorite joke some love, here’s an embed to send you on your way:


JQuery Mobile and Ruby on Rails

When we added a mobile site to Jokels.com, I decided to go with JQuery Mobile. I had recently seen a presentation on it at DevNexus by Raymond Camden and was pleasantly surprised at how easy it was to create a slick, dynamic site for mobile browsers without much coding or design work. In practice, integrating JQuery Mobile into a Rails site proved incredibly easy.  We use Mobile Fu to detect mobile browsers and redirect them to our mobile layouts (.mobile.erb) and have a new mobile-only application layout containing links to the Jquery Mobile stylesheet and javascript file.

For a full explanation of using JQuery Mobile, check out their documentation; it’s astoundingly easy to create rich UIs.  For example: adding a link that looks like a native button is as easy as adding the HTML5 tag ‘data-role=”button”‘.  The strange thing about JQuery Mobile is that it takes over your navigation model and substitutes its own.  It uses this to allow you to do things like put multiple mobile “pages” into a single page and substitute native browser page navigation with an AJAX load of only the target page’s content.  This allows the browser to skip loading most of the DOM or reloading the Javascript engine when navigating between pages.  The effect of this is that JQuery Mobile sites feel much more snappy, almost like a native app.  (Phonegap, an installed mobile platform, supports using JQuery Mobile to create native sites.)

The disadvantage of this is that you lose a lot of your control over your navigation model; there are concerns to note when executing javascript on DOM elements and AJAX loading your own content has some small issues to keep in mind.

I learned the hard way JQuery Mobile works best when you stop fighting its static nature and allow it to provide a dynamic feel as much as possible.  For the full version of Jokels, whenever you click the “random joke” button, a new joke’s HTML is dynamic loaded and inserted into the main page to replace the old one, so my first attempt at the mobile site did the same: 

Here we use JQuery Mobile’s showPageLoadingMsg() and hidePageLoadingMsg() along with JQuery’s fadeIn and FadeOut to hide the page while a new Joke’s DOM is pulled in and the JQuery Mobile effects are applied manually.  Not only is this more likely to break than JQuery Mobile’s navigation but it also means we can’t use certain JQuery Mobile effects which don’t support manual creation, like button groups.

The second pass at this is was much simpler and robust.  Instead of trying to do our own AJAX loading we let JQuery Mobile do the work for us by dynamically creating a link to a random joke each time the mobile page is loaded: 

We don’t include the random joke link in the page creation since JQuery Mobile caches pages already visited: this means including the static link in the page HTML would create a cycle if we managed to loop around to a previously visited joke in a given user’s session.

Page caching brings up another big gotcha with JQuery Mobile: old page DOMs stick around after navigation.  This means that when you’re trying to use a JQuery selector to get a given element that may be on past pages, you need to make sure to select the current “page”‘s element instead of a cached one.  We do this by looking for the class “ui-page-active.” This is how we set the upvote button to a pressed state and update the vote score, for example: 

The final big issue with JQuery Mobile is that it doesn’t support query params.  Ostensibly this is to support page caching: the theory being that “pageA.html?a=1” wouldn’t differ dramatically from “pageA.html?a=2”, so JQuery Mobile strips out query params and treats both links as identical.  Obviously, this isn’t necessary true.  For example, our leaderboards use query parameters to change whether Users or Jokes are being shown, by what criteria they’re sorted and for what time frame they’re being considered.  This means that the same page with different query params can be dramatically different, so we need some way to support query parameters.  One way to get around this is to add the ‘data-ajax=”false”‘ attribute to any links with query parameters, but this loses the beautiful AJAX navigation model.  For the leaderboards we found a really simple solution: embed query parameters in the path using Rails’s dynamic segments routing.  So for our leaderboard links, we just use this snippet in our routes.rb:

Then, in our Leaderboard_helper class we have this small function to get a link to a given leaderboard: 

This allows us to still have static leaderboard pages using our query parameters, but use distinct paths so Jquery Mobile treats them as independent pages and loads them using its Ajax model.

JQuery Mobile has a few minor hang-ups.  But combined with mobile-fu, it allowed us to get a functional mobile version of Jokels.com much quicker than anyone with our mobile experience has the right to do so.