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: '',
    dest: '_assets/'
coffee: {
  compile: {
    files: {
      '_assets/': '_assets/'
concat: {
  dist: {
    src: [
      // other JS files
    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 ='[style]'));
  for (_i = 0, _len = styles.length; _i < _len; _i++) {
    _el = styles[_i]
    style = _el.getAttribute('style');
    if (!(style.indexOf('url(') > -1)) {
    url = style.match(/url\((.*)\)/)[1]; = 'url(' + url + ')';
$(document).on('page:change', function() {

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="" class="twitter-share-button"
data-lang="en" data-size="large"
data-url="{{ site.production_url }}{{ page.url }}"
data-text="{{ page.title }}">
  var js,fjs=d.getElementsByTagName(s)[0];

And add something at the page change event:

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

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...
<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 + '';
    (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) {
<noscript>Please enable JavaScript to view the <a href="">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() {

$(document).on('page:change', function() {

$(document).on('page:restore', function() {

// 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!

Related Posts

Writing cli applications with Golang

Measuring production code coverage with JaCoCo

From Travis Enterprise to BuildKite in 50 repositories