Displaying ads on a site can be a little tricky. Many ad networks simply provide you with a snippet of javascript that must be embedded directly into a page where an ad is to be displayed. These often use document.write to generate more script tags which in turn may generate the actual ad content.

Regardless, sourcing external javascript files in this way can lead to choppy page loads as progressive rendering halts, waiting for this external content to be downloaded and evaluated. Yahoo!’s YSlow grades a page down for this, specifically for violation of Rule 6: Put Scripts at the Bottom.

With scripts, progressive rendering is blocked for all content below the script. Moving scripts as low in the page as possible means there’s more content above the script that is rendered sooner.

In some situations it’s not easy to move scripts to the bottom. If, for example, the script uses document.write to insert part of the page’s content, it can’t be moved lower in the page.

This was causing us serious pains as our ad-provider sometimes responded slowly, and sometimes responded not-at-all. In the spirit of graceful degradation, I crafted this solution for Rails.

Instead of generating the ad markup where the ad was supposed to appear, I first generate a placeholder DOM node, and using a content_for block, tuck the actual markup away. In the application’s layout template (application.rhtml), the last 3 lines are like so.

<%= yield :support_footer %>
  </body>
</html>

Generating the support_footer is straight-forward:

module DeferredContentInsertionHelper
 
  def deferred_insertion to_insert, options = {}
    content = case to_insert
    when String
      to_insert
    when Hash
      render_to_string(to_insert)
    end
 
    container = "di_#{content.hash}"
    defer_container = "defer_#{container}"
 
    content_for :support_footer do
      [ content_tag('div', content, :id => defer_container),
        javascript_tag("$('#{container}').parentNode.replaceChild($('#{defer_container}'), $('#{container}'));")
      ].join
    end
 
    # Insert a placeholder that the real content will replace once its loaded
    content_tag('span', '', :id => container)
  end
 
end

Now, when I want to generate an ad in the layout, instead of issuing:

<%= render :partial => 'ads/banner' %>

I can issue a deferred insertion and get a much more responsive page:

<%= deferred_insertion :partial => 'ads/banner' %>

Note how content generated in the support_footer is initially hidden, and is shown after it has been moved into place.

We were experiencing issues with ads being initially hidden and then moved into place. By leaving them visible, they render properly. The page layout changing as elements are added and removed can be overcome with appropriate css sizing.

One Comment

  1. BryanDonovan

    This is awesome.. thanks! I’ll be implementing this to deal with my ads and bookmark widget in the near future.

Leave a Comment

Enclose code in <code lang="ruby"></code> if you care.
Preview your comment using the button below.