01 4 / 2012

Tagging the HTML tag

Before rails asset pipeline was introduced, I was loading bunch of separate css and js files on every page. Depending on my needs, I would load different set of assets. For example, when I am on the articles/index path, I loaded articles.css and articles.js files (among default ones).

Asset pipeline changed things (for better, of course). Now, all assets are bundled into single application.css and application.js files. This approach was good for a lot of reasons including less requests made to the server (load once, use everywhere) and higher compression rate (when using gzip) all resulting in faster page loads and better ux.


I like to keep things organized. When styling specific page, I want to be very explicit on what I’m styling. Likewise, when writing a script, I want to be very specific when that script should run. Thats why I decided to tag every page in a way that I can reference that info within my css and js files.

In past I tried to do this with html classes, but html5 data- attributes now seem like a way to go.

The idea is to have this:

<html data-controller="articles" data-action="index">
    ...
</html>

This is very easy to achieve by creating custom rails helper method:

module ApplicationHelper
  def html_tag(attributes = {})
    attributes[:data] ||= {}
    attributes[:data][:controller] = controller_name
    attributes[:data][:action] = action_name
    tag(:html, attributes, true)
  end
end

And in the application.html.erb layout file, we simply include

<!DOCTYPE html>
<%= html_tag lang: 'en' %>
  <head>...</head>
  <body>...</body>
</html>

Although a bit controversial, I prefer to generate only <html> opening tag with html_tag method in order to avoid wrapping entire layout file in a block.

Now, if we want to set #content’s background color to yellow only for the articles/index page, we could do this:

html[data-controller=articles][data-action=index] {
  #content { background-color: yellow; }
}

Likewise, we could do this in our coffeescript:

jQuery ($) ->
  return unless $('html').data('controller') == 'articles'
  alert "Welcome to Articles controller"

We can easily add javascript page detection helper method to our App module to simplify this process. It can look something like this:

@App =
  data: (name) -> $('html').data(name)
  controller: -> @data('controller')
  action: -> @data('action')
  path: ->
    p = []
    p.push @controller()
    p.push @action()
    "/" + p.join("/")
  onPath: ->
    for arg in arguments
      return true if @path() == arg
    false

We can rewrite the previous script:

jQuery ($) ->
  return unless App.onPath '/articles/index'
  alert "Welcome to Articles controller, index action"

We can store whatever we like in these attributes. I store vars like module name, current environment, current user’s id and even the public api keys (e.g. for google maps).