Use content_for to Put Markup In Its Place

Posted by on May 3, 2010 in Blog | Tags: , | No Comments

Here is a useful trick for ensuring that you keep your partial templates well organized without sacrificing page-load times or duplicating your layouts unnecessarily.

You can use content_for to capture some markup, but have it emitted into the page from somewhere else.

Two places this is immediately useful: adding additional tags inside <head> and placing in-line Javascript near </body>, while keeping the code itself right next to the DOM elements it works upon.

content_for works by capturing whatever appears inside the block and storing it for later use. You emit whatever is stored using yield. What’s more, content_for doesn’t clobber the previous captured text if you use it more than once with the same key.

Say you have a fancy Javascript control that replaces a standard <select> element in forms. You can do this in your partials:

<select ...>
  <option>...</option>
  <option>...</option>
  ...
</select>
<% content_for :body_close do %>
  <script type="text/javascript">
    // code that does something with that <select>
  </script>
<% end >

And then in your layout, just before the </body>:

<%= yield :body_close %>

Because most browsers will run in-line Javascript upon encountering it, this would delay the execution of the code until all of the page content was loaded, making your page appear to load faster.

What’s more, if that fancy Javascript control also requires a CSS file, but you don’t want to require the browser to fetch it on all of the other pages that don’t need it, you can conditionally add it to <head> by defining another content_for key that accumulates additional markup to go there:

<% content_for :head do %>
  <%= stylesheet_link_tag 'fancy_control' %>
<% end %>

And in your layout:

<head>
  ...
  <%= yield :head %>
</head>

This is also useful for keeping per-page markup like <title> and meta tags in the template and out of your controller.