Google ads: How to lose the wait
Someone once told me that WWW stands for World Wide Wait. But when the page is held because the browser waits for a Google Ad to come in, that’s really annoying. I didn’t want that to happen in my site.
So here’s the story about how to have the page displayed first, ads later. One may argue that you want your money machine up (ha!) before giving away the goods. One could also argue that the ads coming in late will draw more attention. I say: A slow site is like no site.
And as with any story, the solution comes in the end. If you want to know who the killer is, and don’t care about the drama, just skip to the end.
Trial #1: Absolute positioning
Spoiler: This didn’t work for me. But it may work for you. I don’t know if it’s because I’m using a lot of Javascript in my page or not.
The idea was that if the browser doesn’t need to know what’s inside the ad box, it will go on rendering the page. Since an absolutely positioned section is out of the flow, and won’t change any other part’s placement, I hoped that the browser would be smart enough not to wait for the ads. Or stupid enough to overlook the inconsistency in scripting. I was wrong.
Anyhow, this is the code I used:
<div style="position: relative; height: 620px; width: 200px; overflow: hidden; "> <div style="position: absolute; top: 0px; left: 0px"> <script type="text/javascript"><!-- google_ad_client = "pub-xxxxxxx"; google_ad_slot = "xxxxxxxx"; google_ad_width = 160; google_ad_height = 600; //--> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> </div></div>
The outer DIV allocates the necessary space on the page. The inner DIV is absolutely positioned, so no matter what comes in there, everything else will remain in place.
What really happened: IE6 went right on with the rest of the page, Firefox still waited for the ads to load. Even though IE played it nice this time, it’s actually a bug in IE.
The thing is, that I have a lot of Javascript embedded in my page. Since the Google ad code consists of Javascript itself, the browser must wait until that code is loaded before running any other Javascript code (which may, as far as the browser is concerned, depend on it). Firefox waited to prevent unpredictable execution of following Javascript code, IE just went on. Ignorance is bliss.
This way or another, the problem remains. I mean, even Microsoft will fix this bug some day.
(non)-Trial #2:
The idea is simple. If the Google ad code appears just before the closing </body>, the browser must be pretty boneheaded to wait for it. So now the trick is to get the ad in the right place.
Proposed trick: Create a <div> container at the end of the document, and push it to its place with absolute positioning. With Javascript or without. This can be done only if one knows the exact pixel position in terms of the entire document. There is no safe way, that I know of, to get the absolute position of an element by its ID. Neither to get it into a known position within a containing block other than the one you’re currenly inside.
There are scripts out there which claim to do that, but they don’t rely on any standard. And getting a block of ads in the wrong place is too bad to risk.
And I haven’t even mentioned what happens when the page is resized. Unless all surrounding elements are nailed in place with absolute positioning, bad things can happen…
Anyhow, this method didn’t fit my case. I dropped it.
Trial #3: Hijacking document.write()
One widely proposed solution is to hack document.write(), which Google uses to implant the extra HTML. I don’t like it very much, because it’s well, hacky. But I tried. And then I saw what the script in show_ads.js produces:
<script src="http://pagead2.googlesyndication.com/pagead/expansion_embed.js"> </script> <script src="http://googleads.g.doubleclick.net/pagead/test_domain.js"> </script> <script>window.google_render_ad();</script>
So show_ads.js writes a script that loads other scripts? Doing what? Call other?
This was just enough to scare me off. If hijacking means to recursively run scripts loaded by scripts, there’s a good chance to really mess things up. So I gave this idea up as well.
Trial #4: Moving the DOM element
This is the way to do it. Let the browser reach the ad segment at the end of the document, and then manipulate the document tree, moving the element to its right position. This is pretty intrusive, and it turns out that it pushes the browser’s tolerance to the limit.
But first, let’s look at the code. It has two parts. First, we put this where we really want the ads:
<div style="position: relative; height: 620px; width: 200px; overflow: hidden; "> <div id="googletarget" style="position: absolute; top: 0px; left: 0px"> </div></div>
That’s pretty much like what I did in trial #1, with two important differences: The google ad isn’t here, of course, and I’ve given the inner DIV an ID, so I can target it later. Absolute positioning is still important, because I don’t want the page to flicker when the ad comes in.
Then, at the end of the document (before closing </body>) we have:
<div style="display: none;"><div id="googleads"> <script type="text/javascript"><!-- google_ad_client = "pub-xxxxxxx"; google_ad_slot = "xxxxxxxx"; google_ad_width = 160; google_ad_height = 600; //--> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> </div></div> <script type="text/javascript"> document.getElementById("googletarget").appendChild(document.getElementById("googleads")); document.getElementById("googleads").style.display="block"; </script>
That rings an old bell too. It’s exactly like the original script, now embedded in other DIVs and with an extra script snippet, which moves the ads to their right position using appendChild(). W3C is very clear about that appendChild() never duplicates a node, but moves it in case it’s already placed in the document tree.
And the DIV, to which the ads are loaded into, is wrapped by another DIV which is set to “display: none”. This makes them invisible in their original position, so nothing flickers at the bottom of the page and the scroll bars don’t get crazy. And even better, if the appendChild() fails (because the browser is old or primitive), the ads remain invisible. Which is rare enough to neglect businesswise, and harmless in terms of how badly we’ve messed up the page.
In IE6, it was necessary to set the display mode back to “block” after moving it. That’s what the line after the appendChild() is for.
By the way, “visibility: hidden;” wasn’t enough, because the Google data contains an IFRAME, which doesn’t respect the external DIV’s visibility status.
And in the extreme case, in which the browser doesn’t respect the “display: hidden” attribute, we will have some ugliness at the bottom of the page. Functionally, it’s still harmless, and graphically it’s not so bad compared with what such a browser’s user sees every day.
So, this looks like a bulletproof solution. Of course it worked on any fairly updated browser I tested with. What could possibly go wrong?
The answer came to me when I tried in on an Mozilla 0.99 (seven years old). Don’t think I’m using it for myself, but testing on a steam engine is a good way to find weak spots. In our case, moving the ads around caused the browser to display a blank page. All was fine just before the call to appendChild(), but then the page went blank, and the browser started to behave strangely.
This is bad news. I’m trying to make a page load slightly faster, and I might make it unloadable (to some) instead. Even if I don’t believe that someone really uses a browser from 2002, a new, cutting-edge and somewhat buggy browser might fall in the same hole.
The problem, as it turned out, is that the element I move around consists of scripts. Mozilla 0.99 executes the script once when it’s loaded, and once again when it’s moved. Modern browsers execute the script once. IE6 is in the middle: It indeed runs the script once, but loads it twice. Not harmful to the user, but this indicates that moving a script around is not an issue of the past.
It also looks like there was an issue with appendChild() and scripts around the development of Firefox 3.1, but given the relatively little attention it got (and the fact that the relevant remark in the docs has vanished), I suppose this is a non-issue.
To see how the browser behaves, I wrote a small snippet:
<div id="taker"></div> <p>Hello, world</p> <div id="loser"> <div id="node"> <script> alert("Script running"); document.write("I was first!"); </script> </div></div> <script> var node = document.getElementById("node"); alert("Now swapping"); document.getElementById("taker").appendChild(node); </script>
The idea is that the two rows swap places by virtue of appendChild(). The “script running” alert should appear only once, of course. On Mozilla 0.99 it popped up twice, and the page ended up with “I was first” written before and after “Hello world”, which sort-of explains why nothing worked with Google ads. Other browsers I checked, including Konqueror 3.0.0 from 2002 ran the script once, as expected.
Conclusion
The solution I picked was appendChild(), of course. But I also realize that moving around an element which contains a script pushes the browser to its limits. On the other hand, AJAX web sites are becoming increasingly popular, so browsers are more and more likely to support this properly in the future.
For my own use, I’ve decided to do this trick only on browsers I’ve tested. This is possible since the page is produced by a server-side script, which checks the browser version. This will make the page load slightly slower on non-mainstream browsers, but on the other hand, guarantee that the page loads at all.
Reader Comments
Hi
I get this warning from kaspersky saying that
i have a trojan and the object of it is: pagead2.googlesyndication.com/pagead/show_ads.js//show_ads Firefox also it keeps warning me but then after it blocks it.To give you the full thing…
24/01/2010 9:59:54 PM Detected: Trojan.JS.Redirector.ar http://pagead2.googlesyndication.com/pagead/show_ads.js//show_ads Firefox
What next… the antivirus keeps pooping out every 5 minutes,
This is not really relevant to my post, but after googling around a bit it seems to me that it’s a false alarm from your specific software, and that it will be fixed soon (whatever “soon” means)
Is there a case where it wouldn’t work to move around the innerHTML of a div instead of the actual DOM node? For instance, in your simple example above, would this work for all browsers too?
var node = document.getElementById(“node”);
alert(“Now swapping”);
document.getElementById(“taker”).innerHTML = node.innerHTML;
node.innerHTML = ”;
I’m testing this out too, by the way, and I’m concerned that Google might load different ads depending on where you load the ads in the page. Does anyone know if Google does this? It seems like something they would consider.
About the innerHTML thing: I wouldn’t try to “move” the innerHTML data, because this data is maybe manipulated as the script runs. In fact, you copy the data and then delete it, and it’s not really clear to me whether what happens if there’s a script inside. And so on.
But that gave me a completely different idea, which I don’t know why I didn’t try: What happens if the Google ads snippet is injected into the document in the right place through its innerHTML? Will it execute? If it can be assured to execute, then all will be nice. After some googling, it seems like this solution will not work well across different browsers. IE9 has been mentioned as not running scripts injected by innerHTML.
Insights, anyone?
I know some javascript libraries like prototype have a method update() that can be called like this $(‘my-div’).update(newhtml), which actually runs any javascript in the html you’ve added. This makes me believe it’s possible to do this in a cross-browser way.
That said, I’ve read others that say this doesn’t work for Google’s ads because they use document.write, which blanks the page if you use the call after the DOM ready event. It’d be interesting to experiment a bit to see if this could work though.
I’m trying to push ads up into my page, by using javascript and it works on every browser — except now it doesn’t work with IE9. I’m still looking for a solution. If you have one, can you post it?
Thanks!