This site is now powered by Turbolinks


I think that turbolinks is great: it mades it easy to add AJAX PushState to your Rails Applications. The only problem with that is that we can’t use it any WEB application, because it’s a Ruby Gem. So I did some ugly-but-easy hacks and add it to this very site. I will describe the steps above.

First things first

  • This site is OpenSource.
  • I’m using Jekyll alongside with less, bower, grunt and of course, node and npm.
  • You can take a look at these commits to see what I did.

With that said, let’s do the thing.

Well, turbolinks is a rubygem, but it has a lot of dependencies, and Jekyll don’t have the Rails Asset Pipeline, so I can’t figure out how to use this way.

So, to get the pure turbolinks file, I used grunt-curl and compiled it to javascript using grunt-contrib-coffee:

curl: {
  turbolinks: {
    src: 'https://raw.github.com/rails/turbolinks/master/lib/assets/javascripts/turbolinks.js.coffee',
    dest: '_assets/turbolinks.coffee'
  }
},
coffee: {
  compile: {
    files: {
      '_assets/turbolinks.coffee.js': '_assets/turbolinks.coffee'
    }
  }
},
concat: {
  dist: {
    src: [
      // other JS files
      '_assets/turbolinks.coffee.js',
      '_assets/up.js'
    ],
    dest: 'js/up.min.js'
  }
},
uglify: {
  build: {
    src: 'js/up.min.js',
    dest: 'js/up.min.js'
  }
}

As you can see, I get always the last file from trunk and compile to plain old JavaScript. After that, I also concatenate it with other JS files and uglify the result (using grunt-contrib-concat and grunt-contrib-uglify, respectively).

Dealing with style="background-image: url(image.jpg);"

For some reason I’m now quite sure, turbolinks mess up with this kind of style declaration (which I use in the image headers). The solution I’ve found is kinda weird, but it works:

var reloadImages = function() {
  var styles, style, url, _i, _len, _el;
  styles = Array.prototype.slice.call(document.body.querySelectorAll('[style]'));
  for (_i = 0, _len = styles.length; _i < _len; _i++) {
    _el = styles[_i]
    style = _el.getAttribute('style');
    if (!(style.indexOf('url(') > -1)) {
      continue;
    }
    url = style.match(/url\((.*)\)/)[1];
    _el.style.backgroundImage = 'url(' + url + ')';
  }
}
$(document).on('page:change', function() {
  reloadImages();
});

Dealing with the twitter button

The twitter button adds a script to the head, which turbolinks doesn’t replace, so, it gets buggy. To fix that I changed a little the default button markup:

<a  href="https://twitter.com/share" class="twitter-share-button"
data-lang="en" data-size="large"
data-url="{{ site.production_url }}{{ page.url }}"
data-text="{{ page.title }}">
  Tweet
</a>
<script>
!function(d,s,id){
  var js,fjs=d.getElementsByTagName(s)[0];
  if(!d.getElementById(id)){
    js=d.createElement(s);
    js.id=id;
    js.src="//platform.twitter.com/widgets.js";
    fjs.parentNode.insertBefore(js,fjs);
  }
}(document,"script","twitter-wjs");
</script>

And add something at the page change event:

var twttr;
$(document).on('page:change', function() {
  if (twttr) {
    twttr.widgets.load();
  }
});

It was the best solution I’ve found.

Dealing with Disqus load on bottom

I changed the Disqus scripts to only load the commends when the user reach the bottom of the page:

<div id="disqus_thread">
  Loading Comments...
</div>
<script type="text/javascript">
  var disqus_loaded = false;
  var disqus_shortname = 'caarlos0blog';

  function load_disqus () {
    disqus_loaded = true;
    var dsq = document.createElement('script');
    dsq.type = 'text/javascript';
    dsq.async = true;
    dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
  }

  window.onscroll = function(e) {
    var   currentScroll = (window.innerHeight + window.scrollY)
        , elScroll = document.getElementById('disqus_thread').offsetTop;
    if (currentScroll >= elScroll && disqus_loaded == false) {
      load_disqus()
    }
  };
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>

But it causes me some trouble with Turbolinks. To fix that, I simply did:

$(document).on('page:fetch', function() {
  window.onscroll = void 0;
});

And it worked!

Adding nprogress

I also decided to add nprogress, a lib that provides the loading bar medium-style (and youtube-style). I had to add it to my bower.json, add the import in my less file (with a (less) prefix, so it imports it as a less file) and mix it up in my js. I also had to bind the events to nprogress, like this:

$(document).on('page:fetch', function() {
  NProgress.start();
});

$(document).on('page:change', function() {
  NProgress.done();
});

$(document).on('page:restore', function() {
  NProgress.remove();
});

// hides the spinner
NProgress.configure({ showSpinner: false });

And it was working as expected.

The Final Countdown

The result is what you’re seeing right now. I found it neat and it loads really faster. Hope you like it!


Your ad here.

Related Posts

A Repository Graveyard

Charting Repository Stars

GKE in production