Responsive Ads in the Real World: Ad Server Implementation

The last time I wrote about responsive design + advertising I put together a few examples demonstrating how an ad unit could respond to its environment. The demos were fun but in the real world, ads need to integrate with an ad server. This is a slightly greater challenge but I’ve had a few thoughts on it lately and I think it’s not as difficult as I’d originally imagined. So that’s what this post is about.

I’m guessing that most developers have not worked closely with ad servers and many may not be familiar with the technology behind web advertising at all. So before I get into it I’ll say a few brief things about the advertising ecosystem and how ad servers generally work.

Ads are hard

The thing about web advertising is that it’s not a simple two-party handshake. The process is full of intermediary parties and technologies, a summary of which is shown in the adjacent graphic. (Here’s a more detailed illustration of the ad ecosystem… but be warned, it may hurt your eyes a bit.)

See, the challenge with responsive ad models isn’t with media queries or JavaScript, it’s introducing a solution that works for all these parties. There’s a timing element too that makes it more challenging. Responsive advertising is not something a publisher can wade into—the only way it works is for the publisher to have a complete set of “responsive compatible” campaigns. No sane publisher is going to sacrifice ad revenue for responsiveness, so whatever solution appears has to, in a sense, work all at once.

It’s not an insurmountable problem—ad vendors and start-ups will soon figure it out—but it requires a higher level of cooperation than the average innovation, meaning its introduction into the ecosystem will need to follow a different path.

How ad servers work

Publishers that sell display advertising manage ad campaigns through an ad server, which is basically a CMS for ads. Positions or zones for each ad unit are defined on the page and then campaigns are set up in the ad server and configured to flow into the positions based on a number of variables. The rest goes like this:

Usually the publisher (e.g., CNN.com) does not receive the ad directly from the advertiser (e.g., IBM) but from an agency (e.g., Blahblah Interactive Inc.) representing the advertiser. In addition, the ad itself (called a “creative”) is usually not given directly to the publisher but is instead uploaded to a third-party ad server (e.g., DoubleClick). The third-party ad server provides JavaScipt/Iframe “tags” that the publisher then loads into its ad server. Finally, the publisher’s ad server has its own set of tags that get implemented into the site templates, at last delivering the well-journeyed advertisement to its audience.

There’s another piece to the pie that involves ad networks and realtime-bidding demand-side platforms, but let’s leave that to the side for now.

The major players in the first-party ad server world include products like Google’s DART for Publishers, OpenX, Zedo, and 24/7 Real Media’s Open AdStream. The last is the one I’ve worked with the most and one I’m going to use for this article’s example.

So here’s an example of how ads make it onto the page using Open AdStream. We define the site, page, and list of ad units. There’s an optional query variable that we’ll use later. This is an optimized implementation of the ad server’s initial set-up code:

<script>
//Ad set-up for RealMedia OAS ad server
(function() {
var url = 'http://ads.yoursite.com/RealMedia/ads/adstream_mjx.ads/',
  pagetag = 'www.yoursite.com/' + '',
  listpos = 'Top, Bottom, Right1, Right2',
  query = '',
  rns = (Math.random() + "").substring(2, 11);
 document.write('<script src="' + url + pagetag + '/1' + rns + '@' + listpos + '?' + query + '"><\/script>');
})();
</script>

Later in the page, the individual ad units are called in their places with this:

<script>OAS_RICH('Top');</script>
<script>OAS_RICH('Bottom');</script>
...
Responsive-ize it

First let’s mention a few techniques that are not valid solutions. One is always loading the same number of units but hiding certain units for smaller screens. For reporting accuracy and to ensure that advertisers are not being charged for invisible impressions, it’s important that the page only requests ad units that are actually displayed.

Another is user agent sniffing to detect mobile/tablet devices as a way of requesting different ads or modifying layout. On its own, UA-based device detection isn’t necessarily a bad thing but I think most would agree it’s not a natural part of a responsive strategy. Responsive design is the alternative to UA-based device-specific sites. (Although, Luke Wroblewski does make an interesting case for combining the two here.)

Oh, and I should mention that the problem we’re trying to solve concerns ad-based sites running multiple standard IAB ad units: 728×90 leaderboards, 300×250 medium rectangles, etc. That is, a site like Smashing Magazine that combines advertising and a responsive layout is not an example solution to our problem.

Stop resizing your window

I’ll break the bad news to you up front: you have to stop worrying about what happens when people resize their browser. First of all, no one does this. Yes, YOU like to resize your browser and watch DOM elements bend to your will—and so do I—but real-life regular people users don’t. And of course it’s not even possible on mobile and tablet devices.

This whole responsive thing isn’t about passing a CSS fluidity test. So let’s let it go.

The reason this matters is that page views and ad impressions (per unit) are expected to share a 1-to-1 relationship. An advertiser expects to be charged for an impression on a page view event, not a browser resize event. This synchronicity will eventually fade as we move to more AJAX-based content delivery, but for now it’s still firmly in place.

So this technique assumes that ads will be requested once per page load and will be determined by the size of the viewport at the time the page is loaded.

Code: The Set-up

The idea behind this is that we’re going to define a few contexts—mobile, tablet, desktop—and then conditionally serve a set of ads for each context. We start by assigning a list of ad positions and breakpoints based on common device screen sizes:

// Define config
var pageConfig = {
  mobile: {
    positions: ['Top'],
    breakpoint: 480
  },
  tablet: {
    positions: ['Top', 'Right1'],
    breakpoint: 728
  },
  desktop: {
    positions: ['Top', 'Bottom', 'Right1', 'Right2'],
    breakpoint: false
  }
};

We’ll use JavaScript to determine the width of the screen. In the previous responsive ad demos I used matchMedia() but this time I’m going to use document.documentElement.clientWidth.

We’ll pass the width to a function that will evaluate it against our breakpoints and determine a context.

// Function to determine screen context
function setContext(screenWidth) {
  var mobile = pageConfig.mobile.breakpoint,
    tablet = pageConfig.tablet.breakpoint,
    type;
 
  if (screenWidth <= mobile) {
    type = 'mobile';
  } else if (screenWidth > mobile && screenWidth <= tablet) {
    type = 'tablet';
  } else {
    type = 'desktop';
  }
 
  return type;
}
 
// Get screen width
var screenWidth = document.documentElement.clientWidth;
 
// Set screen context
var screenContext = setContext(screenWidth);

Now we can pass what we know to the ad server set-up code. First we turn the list of positions into a string and give that value to the listpos variable. We also set the query value to the context type. This allows us to repurpose the same ad position names for each context.

// Set list of positions
var adPositions = pageConfig[screenContext]['positions'].join(',');
 
// OAS set-up code
(function() {
var url = 'http://ads.yoursite.com/RealMedia/ads/adstream_mjx.ads/',
  pagetag = 'www.yoursite.com/' + '',
  listpos = adPositions,
  query = screenContext,
  rns = (Math.random() + "").substring(2, 11);
 document.write('<script src="' + url + pagetag + '/1' + rns + '@' + listpos + '?' + query + '"><\/script>');
})();

That’s all the code we need to conditionally request ads based on screen size. Note that this code must go in the head and should be executed as soon as possible.

Ad positioning

Now that we’ve requested the right batch of ads we have to position them appropriately on the page. In almost every case I think this can be handled with CSS alone. Unused ad units can be hidden safely with display:none because the unused ad units were never returned from the ad server. And units can be floated or absolutely positioned to accommodate different layouts.

But what if CSS isn’t enough? For example, what if a mobile layout requires an ad unit to be placed somewhere else in the DOM structure? Well we can use JavaScript to move an ad from one location to another. But it comes with an important caveat: JavaScript containing document.write cannot be moved around the DOM. The JavaScript will re-execute when inserted into the DOM and the document.write call will flush the page.

You can only reposition the HTML contents generated by the JavaScript. This requires some cooperation with the code coming from the ad server. If you’re hosting the ad creative (i.e., not using third-party tags) this shouldn’t be a problem. Within the ad server you can wrap the creative in a div and your JavaScript will just grab that div. But if the creative is served via a third-party server you’ll need the same cooperation on the client’s end… which can be difficult. And there may be cases where the ad contents cannot be isolated from its deployment code.

The good news is that mobile creatives are much simpler than Desktop creatives (no Flash) and so if we’re doing this for the mobile context it may not be a problem. Assuming we’ve cleared those hurdles we can proceed as follows.

First, give each ad unit a class and then within your mobile media query block set that class to display:none. This will hide all ads in the mobile context by default.

Second, insert a placeholder div into your HTML wherever you want the mobile unit to appear. Set this to display:none for all contexts.

Next we’ll use JavaScript to move the ad from its original desktop/tablet placement to the mobile-only placeholder div. The previous code was done with plain ‘ole JavaScript but for this I’m going to use jQuery to keep the example from getting too verbose.

$(function() {
 
  // Reference to the new location
  var $newPosition = $('#mobile-ad-placeholder'),
 
    // Reference to the CONTENTS of the original ad placement
    $originalAd = $('#ad-top .ad-contents');
 
  // Move the ad to the placeholder
  $newPosition.append($originalAd).show();
 
  // Remove the original ad from the DOM
  $originalAd.remove();
 
})();

Pretty simple, right?

Wrap-up

Clearly this isn’t a blanket solution for all responsive advertising scenarios; it’s only a possible solution for a particular ad server. And it’s only solving the problem of execution on the front end. But I think it lays the groundwork for approaching the general problem of getting responsive design and advertising to play nicely together.

To review:

  • Define a set of contexts and associate each context with a breakpoint and a list of advertising units.
  • Using JavaScript, use window.matchMedia or one of the many available properties to detect client screen width and determine the context.
  • Modify the request(s) to the ad server to only include the ads needed for the current context.
  • The context is determined by the size of the screen at the time of page load. Forget about what happens when the browser is resized. It doesn’t matter.
  • If you’re smart about your mark-up structure, repositioning and hiding ads can be handled with CSS. If you must relocate an ad in the DOM it can be done with JavaScript—with the stipulation that you can access the contents of the creative independent of any implementation code that uses document.write.

Thoughts? Ping me on Twitter at @robflaherty, discuss on Hacker News, or leave a comment below.

Both comments and pings are currently closed.

Discussion

Josh Duck

I wrote a script a while back that allows you to move HTML/script tags and not have document.write destroy the page https://github.com/joshduck/Injector.js

This is valuable information for anyone working on ad-supported sites, and this appears to be a legit solution to responsive advertising; simply hiding ads on smaller screens is not one of them.

Hi Rob,

Very happy to see you continue thinking about and working on this topic/challenge. Very important. Keep it up!!

I’ve been looking at Ghost Writer for handling the document.write craziness..

There is also writecapture but I’ve found issues with it.

Quick question on the document.write issue: If you’r simply reshuffling the content on the page using CSS , will it still cause the javascript to run again? In my mind it shouldn’t; but one can never be too sure. Thanks for the article.

“you have to stop worrying about what happens when people resize their browser. First of all, no one does this. Yes, YOU like to resize your browser and watch DOM elements bend to your will—and so do I—but real-life regular people users don’t. And of course it’s not even possible on mobile and tablet devices.”

Well, one could argue that tablets and smartphones are the most important use case for re-sized viewports if the device is rotated, which is quite common. For desktop browsers: Yes, nobody resizes their browser window.

@RiaanP,
As long as you’re not removing and inserting JS into the DOM, JS won’t re-execute. CSS changes to elements containing JS shouldn’t have an impact on the JS.

@Eric,
Yes, absolutely. Device orientation changes are important. I think the solution to this as far as responsive ads are concerned is to try to avoid establishing ad breakpoints between common orientation sizes. So that a device gets the same set of ads in both orientations.

@Eric and @Rob Flaherty,
I think the proposed solution, trying to avoid ad breakpoints between common orientation sizes, is practically impossible. If I look just at a few devices currently on my desk I see many overlapping resolutions, like 480×800, 768×1024 and 600*1280. Unfortunately, there are no common orientation sizes.

Etai Koren

Rob, I’ve checked your implementation and there is an issue with it. Setting an element CSS to display: none will still make the browser fetch it (even thought its not shown) causing false analytics. I think a better solution should be Responsive Javascript. What do you think? I’ll be happy to discuss this with you.

@Vasilis,
I agree that trying to avoid having breakpoints between orientation changes is a losing battle. But it’s really a minor problem anyway.

@Etai,
Display: none is not being used to prevent ads from showing. “Unused ad units can be hidden safely with display:none because the unused ad units were never returned from the ad server.” The ad units hidden with CSS do not contain any ad code.

In the repositioning example the only ad that uses display: none is shown once it’s placed in the new location. So the analytics will be correct :)

I agree with what you’re saying with browser resizing in that it is a niche case, but when this does happen for legitimate reasons, how should you handle it?

The main case in point being a full width leaderboard ad served on desktop size resolutions. If the user were to resize their browser down to half that size, what should the ad do – would it simply be cropped, or proportionally scaled (I’m guessing hiding isn’t an option for legality reasons?). It differs from a standard MPU which you can naturally reflow somewhere in the document because of leaderboards being larger in width.

I’m actually working on a fairly high profile rebuild currently, and the single biggest obstacle to using responsive is the advertising aspect of the project – and it seems it’s a case of looking for our own tech solutions to bridge the gap because advertisers aren’t currently attempting to do so.

Hi Rob, I’m not familiar with “Open AdStream”, so I was wondering why the ad code has to be in the “head” of the document? Doesn’t this cause the page to load slower, (the old Google Adsense dreaded page load problem)? I am trying to find a solution that doesn’t add script to the head, but all the Ad servers seem to require it be placed before page completion for confirming page placement (to stop fraud?).

@Dwight,
The way this Open Ad Stream implementation works, a single request is made to the ad server requesting all of the ads on the page at once. The server returns a script that contains a block of code for each ad unit. The individual ad units are function calls that reference this script. And the scripts are written to the page using `document.write`.

While I don’t think it’s a requirement that the script be loaded in the HEAD, the script does have to be loaded high up on the page in order to ensure that the code is available for the first occurring ad unit.

Thanks Rob for the reply. The “document.write” part is typical of what the ad servers use (require) for ad placement. Unfortunately, on small or simple mobile pages, the ad is reached well before other async javascript has had time to fully load. That is, unless you force it to load before the ad, which of coarse then slows the page down even further. So in order to have javascript determine the page size you want an ad for, you must load that code before the ad, run it and then load the ad. And this code won’t have the benefit of a javascript framework either (since you would have to have that loaded too).This may not sound like a big deal, but on mobile devices it does seem to add annoying seconds of download time to page generation. IMHO, this will continue to be a real sticking point for responsive web ad placement until ads are served in another manor. At present, the lag time is a big penalty.

Dwight,
There’s very little JavaScript needed to determine the screen size and which ads should display. I’d be very surprised if it had a noticeable impact on page rendering, even on a low-end mobile device. Do you have an example URL?

True, the specific code to just get screen size is minimal. But if one tracks cookies (for a specific screen size preference), or adjusts for orientation, or dynamically adds css on demand, you start adding more code.

Actually, for me the problem comes into play because I load all but the initial config code asynchronously. So the page html can easily be loaded way before the scripts used to verify and react.

So with a typical synchronous script load via the document head, there wouldn’t be a problem other than load time. ?But loading async is way faster, with the page appearing very quickly. It may be several seconds before all page functions are available, but most users don’t care once they have something to look at. Thus, Adsense code is still a huge drag given it has to load fully before the page will finally display and you can’t change ad size after it has started. And as far as I know, you can’t get ads to resize if a user changes the window size?

very nice proposition indeed.
btw your proposition for css- translating the ad creative can be generalized to the desktop: it allows faster rendering of your site while still maintaining the synchronous doc.writes calls :
place all ads at the bottom of your body, but still synchronously called. and move them by css . that will avoid blocking your site optimized loading.

example available on http://www.matvpratique.com with SMART adserver . used to have it with openadstream too.

as a side note for the asynchronous-inclined : as far as i know, async ads rely on injecting iframes thus allowing third-party doc.writes. It seems to be the perfect solution. until you need to allow expandable creatives : those need to outgrow their frame containers ….