A Simple Frontend Strategy for Mobile Webapps, or, How we Built Hacker News Mobile

September 19, 2011
Development

HN Mobile vs. Regular Site

Last week we released a mobile client for the website Hacker News. We've been huge fans of the site since our earliest startup days and most of us check up on it at least a couple times a week (ha!). The site is clean and simple, but depends on tables for layout, so it doesn't deliver the best experience on mobile phones.

When we discovered that an API had been generously built and made public for the site, we decided to take a crack at building a quick mobile website against the API.  In doing so, we found a couple of great tools that we thought we would share.  They made building the site quick and easy.

The Approach

The app is built (almost) entirely client-side.  Kris has a part II on the way that covers some server-side caching we do.  This part of the post covers my role doing the front-end development.

Because this app is client-side built, it is fully dependent on JS to work correctly.  As a big believer in the idea of One Web this isn't something  I would normally do, but there are enough HN client alternatives that building a JS-free version wasn't worth the effort.

Additionally, a client-side only approach gave us the chance to try a few things we've been wanting to do with mobile HiFi sites. Since HiFi lets you access all of its content via a JSON API, it works as a great platform for mobile sites or Phonegap based Apps.

The goal was to basically write HTML/CSS like I always do but have it connect to remote data. Here were the frameworks/libraries used to make this work:

This might seem like a lot to keep track of, but really each component is pretty small both conceptually and in filesize.  I went with this approach over using something like jQuery mobile because my main goal was to get this to perform well.  I've experimented a good deal with jQuery mobile and I have not been very pleased with it so far.  Scrolling is choppy (I assume because of DOM caching?) and instead of writing CSS against my own HTML, I've got to figure out how to do it against jQuery Mobile HTML and preexisting CSS.

By essentially building a simple webpage with this app, I keep scrolling as smooth as possible and performance high.

The services I used in building this app are:

  • Unofficial HN API
  • ViewText (Give it a url, it returns clean HTML)
  • getFavicon (Give it a url, it returns a Favicon)
  • HiFi API (CMS for Static Content API/Programming Environment)

Most are pretty straightforward.

The HTML/Templates

Rather than fiddling with crazy inter-page animations, I stuck with a simple fade that was driven by CSS animations. These are generally much smoother than JS animations and they are easy to write.  Here is the overall page structure of the app:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Hacker News Mobile</title>
    </head>
    <body>
        <header id="header" class="container">
            ...
        </header>
        <div class="container loading">
            <p>Loading...</p>
        </div>
        <section id="pagebody">
            ...
        </section>
        <footer id="footer" class="container">
            ...
        </footer>
    <!-- templates -->  
    </body>
</html>

Essentially, there are a few static components (header/footer), but the content of each page exists in #pagebody. On each page change, the following steps are taken:

  1. Add 'emtpy' class to #pagebody which fades out the content
  2. Make AJAX request
  3. When complete, swap the new content into #pagebody
  4. Scroll to the top of the page if the page change wasn't caused by a 'back' event
  5. Remove 'empty' class from #pagebody which fades it back in

An awesome side effect of this approach was that most browsers seem to remember scroll position on back events.  Additionally, the DOM is always really tidy which keeps scrolling fast.

The templates are built with jQuery Templates.  In all honesty, the templating language isn't great, but it is pretty fast.  For a more complex project I might choose something like mustache.js.

The template for the text view looks like this:

<script type="text/x-jquery-tmpl" id="text" class="script-template">
	<article class="text container">
		<h1>${title}</h1>
		{{html content}}
	</article>
</script>

Nothing too crazy.

The Javascript

The structure of the Javascript for this project is based on Hasher.js.  I found this project while Googling for something like "Hash Router" or "Hashchange Callback". I was going to use something like Backbone or Knockout, but my main interest in the project was building it, not learning a new framework.

Hasher.js is really slick.  Here is how it works, which is about all you need to know:

// Add a route
Hasher.add("/name/:name", function(name) {
    // Alert
    alert(name);
 
    // Console
    console.log(name);
});
 
// Setup the Hasher
Hasher.setup();

Basically you set up routes much like you do in server-side frameworks (hello Slim!) that get called when the URL Hash changes.  For this project there are just a couple of routes:

  • / - home listing
  • /page/:id - pages 2-n
  • /comments/:id - comment pages
  • /text/:url - text-only view
  • /about - about page

Here is an example Hasher route that grabs content from the HiFi API for the about page:

Hasher.add('/about', function(){
    hn.$page.addClass('empty');
    $('html, body').animate({ scrollTop: 0 }, 'slow');
    $.ajax({
        url: 'http://www.gethifi.com/hifi/api.js',
        dataType: 'jsonp',
        data: {
            q: '{"type":"page","url":"/demos/about-hacker-news-mobile","count":1}'
        },
        success: function(results){
            hn.$page.empty();
            $.tmpl(hn.templates.about,results.results[0]).appendTo(hn.$page);
            hn.$page.removeClass('empty');
        }
    });
});

Since these all pretty much have the same flow (hide content, fetch content, show content) I abstracted that process and ended up with something like this:

// Comments
Hasher.add('/comments/:id',function(id){
    hn.changeAjax({
        template: 'comments',
        url: 'http://hn.gethifi.com/api/post/'+id
    });
});

Pretty clean! In total, there are only about 100 lines of JS for this whole thing.

Tips and Tricks Learned

For the most part, this was pretty straightforward.  It isn't hard to go from plain HTML to jQuery templates, and it's just as easy to work with Hasher.js.  That said, there were a couple of neat things that others pointed out or I figured out during this process that are worth noting.

Mobile 'click' Events are slow, use jQuery 'vMouse'

It turns out that using the click event is pretty slow on touchscreen devices.  After the tap, there is a 200ms delay before it is triggered.  To make your mobile site as responsive as possible, consider using the vmouse plugin that the jQuery Mobile folks built.  After it's installed, rather that using 'click' or 'mousedown', use 'vclick' or 'vmousedown'.  It cleverly uses the best event binding for the circumstance.

Don't bother with jQuery Animation

If you've got CSS animation available, use it.  It runs much, much smoother and it makes your application much simpler.  You just need to add a class and your animation happens declaratively.

On Small Apps, Pre-compile your JS Templates

I made this super easy by giving all of my templates the same class.  At the top of my JS, I ran this which pre-compiled them and dropped them in an object:

hn.templates = {};
$('.script-template').each(function(){
    var $tmpl = $(this);
    var name = $tmpl.attr('id');
    hn.templates[name] = $tmpl.template();
});

This made it easy to quickly write a template and use it right away.

Overall, it was a really fun project to work on because it ended up being something I'll use personally just about every day.  If you've got any questions on the project or suggestions for improvement, let me know.

Kris will be writing up the server-side part of this in the next day or two which should be really interesting. We ended up caching the unoffical HN API for performance using Redis and Node.js. Stay tuned!

Comments

Sistema's avatar
Sistema
Você deseja conseguir muitos cartões de credito válido e não sabem como? descobri como conseguir a senha do sistema privado
só clicar com botão direito em cima da pagina e mandar exibir o código fonte e a senha estará lá
Dentro do site voce segue as recomendações que receberá o que deseja. Vou arrumar uma grana
http://www.sistemaprivado.org
Quem nao achar a senha que achei é 'SIGILOSO'
raj's avatar
raj
Really nice article.
Joel's avatar
Joel NMC team member
Tambourine Man,

Which color scheme are you referring to?
Kurt's avatar
Kurt
Wow...excellent call with vmouse. Serious in-app performance boost.
Tambourine Man's avatar
Tambourine Man
Nice stuff.
Tangentially related, care to share the color scheme with us?
Thanks

Leave a comment