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
    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(
  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];
    _el.style.backgroundImage = "url(" + url + ")";
$(document).on("page:change", function() {

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 }}">
  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 = '{{ site.disqus_shortname }}';

  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) {
<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!

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!