Cleaning Up Ad Server Scripts
If you work on sites with ads you know that few things can muck up your efforts to improve a site’s performance like an ad server–3rd party ad tags, iFrames, lots of
document.write, and almost all of it out of your control.
In his P3PC series, Steve Souders is currently analyzing the performance of popular 3rd party content, including some ad server implementations. In the same spirit I’ve taken a critical eye to the ad implementation code for 24/7 Real Media’s Open AdStream (OAS) ad server, an ad server I’ve been working with for a few years. It does some things very well but like a lot of ad delivery engines, the code can get a little ugly.
Of the several implementation options that OAS offers, the most sophisticated, the one recommended for Rich Media delivery, and the one we’re going to look at is the “MJX” method.
The nice thing about this method is that all of the ads on the page are retrieved from the server in a single request. Unfortunately, the ads are written to the page with
document.write, which blocks other page components and limits our options for asynchronous loading.
Original Implementation Snippet
Here is the MJX implementation code as specified in the OAS documentation. The majority of the code sets up the call to the ad server; the last two lines are the local calls for the individual ads.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
As you can see there’s a lot of room for improvement here. First let’s take care of the easy stuff.
- It’s no longer 1996, so we can get rid of the HTML comments inside the scripts.
- For the same reason we can also remove the language attributes on the script elements.
Now let’s look at the
Next, the script browser-sniffs for Netscape Navigator 3 and what I think might be IE 4 (!). Just to remind you, these browsers were released in 1996 and 1997, respectively.
The next script block defines the function called in the ad unit calls. If
OAS_version = 11, the ads returned in the aforementioned request are loaded. Otherwise the previously defined
OAS_NORMAL function is called, loading static link-and-image ads.
Somewhat amazingly, the majority of the code is focused on providing compatibility for browsers that have been extinct for a very, very long time. It’s unfortunate, in 2010, to see code like this in official documentation for a major ad server. The good news: unless you have a lot of users accessing your site via a time machine, this is all code that can safely be removed.
Revised Snippet (In which we reduce 35 lines of code to 7)
OAS_targetapplied only to the fallback simplified implementation, that was removed.
- Line 10 provides a fallback for browsers that don’t support the
randommethod, so that was removed.
- Lastly, the function name for the ad calls was changed from OAS_AD to OAS_RICH since we no longer need to support an alternative to the OAS_RICH function (defined in the script returned by the main call to the ad server).
- Update: In the comments, kangax pointed out that the line producing the random number could be further improved by simply type converting the number instead of creating a String object. He also mentioned that we can remove the split of the opening script tag.
The set-up code can be reduced even further if you’re not using query strings to pass keyword data to your campaigns.
My tests revealed no problems with the revised version but I strongly recommend conducting your own thorough tests before making major changes to your ad code.
While these changes do not address the main performance problems of the ads—namely,
document.write— they do clean things up significantly. And I think that’s always a good start.
Out of curiosity…
Why is the <script> tag split in the document.write?
Nice article. I’m in the process of cleaning up an OAS script too. I’m using createElement() and setAttribute() in place of document.write() and it has performed well in my tests so far.
The script tag is split like that to prevent the browser from parsing the string as a script element. I’m actually not entirely sure that it’s necessary anymore.
It’s the use of document.write in the OAS_RICH functions that is the obstacle, so calling those functions on window.onload unfortunately wouldn’t work. (Sorry, I realize I did not explain the problem clearly in the post.) But I think there’s still a way to do it… subject for a future post. =)
That’s great. Are you using the MJX method or one of the others? How are you working around the document.write insertion code that the ad server returns?
You know, Rob.. I recently attempted to “help along” remotely loaded scripts that use document.write with a little jQuery plugin called “loadAdScript”:
It appears to work well in testing, but I haven’t yet been able to test it out in production.
A year or more ago I played with the idea of asynchronously loading the mjx call and overriding the document.write function so that it would queue up html into a variable until I flushed it out and it would work for simple ads but once you start trying that with ads that write out their own script tags with document.writes in them it got messy since some browsers wouldn’t load those scripts until later (I had to parse out the script tag and load it via dom insertion).
Another idea I had was to load the mjx tag at the bottom (allowing the content of the page to load and become usable while loading the ads late) and write ads into divs that could then be moved into place. I didn’t really play with this out of fear of expandable/pushdown ads.
That’s great! It sounds like just what I was looking for. I’m looking forward to checking it out.
I think you identified the biggest hurdle, 3rd party tags that use document.write to insert their own scripts. That’s the thing that I fear makes serving the ads asynchronously near impossible. Although it sounds like you got pretty close to a solution!
You can shorten it further by replacing `new String (Math.random())` with `Math.random() + ”`. No need to create a *string object* just type convert number to string
I would also wrap this whole thing with self-executing function and gotten rid of those “OAS_” prefixes in variable names, but that’s not a big deal.
Also, AFAIK, there’s no need to split start `script` tag; it’s end tag that needs to be escaped (well, technically—as per, say, HTML4.01— script element is terminated on occurrence of “</" but none of the browsers I've seen respect that, and instead close it on "</script"). And you already have end tag escaped properly as "”.
Thanks for the recommendations! I updated the post to reflect most of them. I agree that wrapping everything in a self-executing function and shortening the variable names is a good idea, but, in the context of the blog post, I thought it’d be better not to alter the variable names to avoid any confusion with the OAS documentation.
thanks for the article.
I’m currently working also on the document.write problem and have found a good solution for me.
As mentioned already by others, I load the advertising at the bottom of the site.
For this I have written a php function that I run at the points of the site. This function writes an ID in a global array and echos a container with this id. Like this:
The solution increases the loading speed of the site many times over.
I hope someone helps this solution and you can read this text. Since my English is not so good, I translated this text with a google.
If you click on my name, you can see my solution in action.
Thanks for the information. I’m still not sure how you get around 3rd party ad script that includes document.write. I’ll take a closer look at your site to see if I can figure it out.
Wow, nice work!
Another optimization would be to omit
Also, why are the ‘ad calls’ in their own
scriptblock? Why not just add them in the same block as the rest of the code?
FYI, you might be interested in this — I recently optimized the asynchronous Google Analytics snippet.
Good question about the ‘ad calls’. I didn’t make it clear in the post, but they occur later in the page, where the ads themselves are placed. The
OAS_RICHfunction uses document.write to insert the ads into the page.
Just a left a comment on your Analytics snippet post…
I understand the ad calls are separately placed in the document where the ads should occur. Still I think you can include the first ad call in the main
scriptblock already, if you just place it where the first ad should appear, since the snippet is using
document.write()(which blocks further script execution until the file is loaded anyway).
As for the safety of omitting the
typeattribute — I’d be very interested to see that discussion. As far as my tests go, everything seemed to work perfectly without it in all A-grade browsers.
Ah, I see what you mean. I agree that would work, but for the sake of clarity, I didn’t want to stray too far from the implementation code as it’s presented in the OAS documentation.
Now that I think of it, the debate I referred to about the
typeattribute was between you and kangax on his blog. =)
You can definitely use some post-loading techniques. Make emtpy divs on your page with ids of ad_pos_1, etc and then place this as your last content on the page before .
//repeat for each ad
Causes slow ads to appear out of nowhere, but at least the users can see the content.
Yes, the method you describe works but it’s not actually post-loading the ads, which I think is the ultimate goal. Still, I agree it’s an improvement. As ddunlop mentioned previously, one concern would be possible complications with push-down and expandable ads.
As the “time-machine” is back with IPhones and Ipads… does this refinment to show only rich media still apply?
I would strongly caution against the use of Willf’s suggestion to move the ads after they’ve been loaded — Gecko and WebKit will reload an iframe when it’s moved within the DOM. This is going to lead to double-counting and an increase in overall load times. https://digital-fulcrum.com/iframe-reloading-behavior/
Take a look at ghostwriter which provides asynchronous loading of scripts that use document.write. With it, you can post-load these ads pretty easily.
I’m new to this… I assume when you implement this method, you are using jsx instead of mjx.
if ad_pos_1 is the container where the ad should show up, what would ad_1_hidden mean here? Please let me know, I need to implement oas using jsx method.
Thanks very much for this solution. I got the mandate today to implement this OAS code and when I looked at it, I felt like I it was the mid-90’s again. I used your solution and it’s working fine.
I want to respond to the post from Will Alexander. First, if you’re going to promote a paid service (ghostwriter) that you work for, you *have* to disclose it. Otherwise, it’s just free advertising and not above board.
Second, your comment about iframe may be valid, but this ad server does not use an iframe. It’s possible that a given implementation might use that HTML tag, but mine certainly does not. So, you’re spreading FUD for no good reason.
@BillC I think you are mistaken.
Ghostwriter starts at 10,000k for one site (price not posted on their site, by the way), so he has 10,000 very good reasons, and he get a couple link backs to the site of which he is a “Partner”.
It’s not above board and speaks volume about the company’s ethics.
However, this article was great and its nice to see developers helping developers purely out of fraternity.
Has anybody tried using the SX tags and iFrames? I’m thinking about dynamically creating the iframe and inserting it into the mark up after the page has fully loaded. What is the downside of doing it this way?
Sorry for the late reply. I haven’t tried using SX and iFrames, although I have experimented with the idea of using iFrames with the MJX tags. It’s a little janky but I believe it works. If anyone is interested in it I could write up a quick blog post about it.
We have built a script that uses the SX tags and Iframes to async load the ads. Works well but found an issue using companions with the SX tag, this is from realmedia,
“companions not displaying together
all the time is due to something called a race condition that can have an
affect on the coordination of SX tags”
Because the SX tags don’t do a single call to all of the ads on the page like the MJX do.
I know this is a bit old but I would like to give my 2 cents here:
you should be able to use SX tags and still be able to use companion positions as long you declare all positions on each tag, ie:
Banner 1 – @Top,TopLeft!Top
Banner 2 – @Top,TopLeft!TopLeft
have you tried that????
Great article. I love the idea of tackling advert serving code rather than leaving it to rot, as so often happens. The problem with legacy code is that; as it gets older, and the issues it was designed to address become forgotten about, it becomes even harder to understand and developers are even more worried to touch it in case they break something.
I’m glad though that you emphasise the importance of testing after refactoring. I don’t think that can be emphasised enough!