13

So, I'm trying to find an answer to why this problem is happening; I've fixed the problem, but I want to know why it happened.

TL;DR

Google-provided conversion tracking code that injected an iframe using document.write suddenly caused the page to cease to execute in all versions of Internet Explorer, but was remedied by injecting the same iframe using a non-document.write method.

The Story:

Doubleclick is an advertising network that provides a JavaScript snippet to track conversions from ads.

The snippet they give looks like this:

<SCRIPT language="JavaScript">
var axel = Math.random()+"";
var a = axel * 10000000000000;
document.write('<IFRAME SRC="https://fls.doubleclick.net/activityi;src=143;type=donat01;cat=indir4;ord=1;num='+ a + '?" WIDTH=10 HEIGHT=10 FRAMEBORDER=0></IFRAME>');
</SCRIPT>
<NOSCRIPT>
<IFRAME SRC="https://fls.doubleclick.net/activityi;src=143;type=donat01;cat=indir4;ord=1;num=1?"
WIDTH=1 HEIGHT=1 FRAMEBORDER=0></IFRAME>
</NOSCRIPT>

Now, I know that, for all sorts of reasons, document.write is hazardous and should be avoided. But, Google is giving me this code, so, I figured I could trust it.

It suddenly started breaking all of our pages for all users using Internet Explorer. As in, the page would stop rendering entirely once it hit the document.write. This was crazy: One of the largest third party advertisers on the internet had given me JavaScript that had LITERALLY broken my purchase pages for 25% of my traffic!

As triage, I quickly substituted in the same code using the injection technique found in Google Analytics:

var iframe = document.createElement('iframe');
iframe.src = //the URL;
iframe.width = 0;
iframe.height = 0;
iframe.frameborder = 0;
var ref = document.getElementsByTagName('script')[0];
ref.parentNode.insertBefore(iframe, ref);

This resolved the problem, without actually explaining:

Why does a nearly empty iframe, injected using document.write, break Internet Explorer, but this method above doesn't?

Yahel
  • 37,023
  • 22
  • 103
  • 153
  • 11
    Probably because you shouldn't be using `document.write`, `iframe`s, or Internet Explorer :) – jtbandes Jul 03 '11 at 04:41
  • Couldn't agree more on all three counts :) Sadly, 25% of my audience uses IE, and this document.write iframe code is used WIDELY on the internet, but this problem does not appear to be otherwise documented, AFAIK. – Yahel Jul 03 '11 at 04:46
  • 3
    Oh, please. Internet Explorer completely breaks **itself**. – Lightness Races in Orbit Jul 05 '11 at 14:12
  • @yahelc: I can assure you that no "document.write iframe code" is used "WIDELY" on "the internet". – Lightness Races in Orbit Jul 05 '11 at 14:13
  • 1
    @TomalakGeret'kal this is the tracking code for Doubleclick floodlight tags. Some variation of this tag appears on 14.5% of the top 10,000 websites. http://trends.builtwith.com/ads/DoubleClick.Net Though there are a couple of variations, this is the most widely used. – Yahel Jul 05 '11 at 15:17
  • @yahelc: I count that as _one_ use, and I do not think of companies like DoubleClick.Net as setting a high standard on web development! – Lightness Races in Orbit Jul 05 '11 at 16:31
  • 2
    @TomalakGeret'kal well, I couldn't agree more on that count. – Yahel Jul 05 '11 at 19:53

6 Answers6

8

I've solved the problem; it turns out that it had nothing to do with the contents of the <iframe>.

It turns out the page is served by a framework that began using a backend DOM parser that, for reasons likely related to the presence of </ within a <script> tag within the document.write, completely removes the </iframe> closing tag from the generated page, even though it preserves it in the backend. (It's probably trying to enforce ETAGO rules).

The reason I was able to reproduce it was because I was copying the generated document.write code, not the original code, and never noticed the missing </iframe>. (And my "functioning" document.write code didn't have the stripped out </iframe> tag, leading me to believe that the problem was the contents of the iframe.)

As a result, browsers parsed an unclosed <iframe> tag on the page, which Internet Explorer didn't know how to handle, and died part way through the parsing of the iframe (I'm still not totally sure why).

Community
  • 1
  • 1
Yahel
  • 37,023
  • 22
  • 103
  • 153
  • 1
    Did you try placing the matching `document.write("")` at latter points of your page to see what happens? – hugomg Jul 11 '11 at 03:52
  • Bug goes away. So, it's definitely the missing end tag that is the culprit; at this point, I'm just curious why IE dies, while other browsers cope. – Yahel Jul 11 '11 at 04:01
  • Interesting! Is the framework a publically-available one? Certainly leaving an ` – bobince Oct 31 '11 at 23:56
  • @bobince no, but, it uses PHP's `DOMDocument` to parse the DOM, which is where the behavior gets introduced. I'll try to see if `\x3C/` prevents the parser from freaking out. – Yahel Nov 01 '11 at 00:37
  • 1
    Confirmed `DOMDocument::loadHTML` exhibits this behaviour. It appears to try to fix up the invalid script block containing `` with a CDATA Section, but it loses some content in doing so, probably accidentally. Yeah, if you're feeding DOMDocument some HTML you'll have to make sure the incoming HTML is actually valid; any of the ways of avoiding writing a direct ETAGO are fine. – bobince Nov 01 '11 at 17:42
0

document.write() blocks further page rendering until it finishes. I assume that the remote script was taking a while to load, and thus blocking the rest of the page from loading.

I also assume that Math.Random() function doesn't help matters.

Also...Google's tracking codes scare me...they tend to be ugly hacks of javascript.

timw4mail
  • 1,716
  • 2
  • 13
  • 16
  • 1
    The `Math.Random()` is just for generating a cachebusting attribute onto the URL. Speed doesn't seem to have been the issue here; the iframe URL responds at a perfectly normal rate in all other browsers in both conditions. – Yahel Jul 05 '11 at 15:31
  • @yahelc What other javascript do you have on the page? It's possible there's an error killing javascript parsing before this iframe code in IE. – timw4mail Jul 05 '11 at 16:40
  • no errors on the page, and confirmed using IE Dev tools and Firebug Lite that the DOM parsing just dies at the `document.write`. I'll put together a simple demo in a bit. – Yahel Jul 06 '11 at 16:31
0

There is 2 reasons why the first method should be slow.

  • document.write() blocks until it is actually performed
  • the window’s onload event doesn’t fire until all its iframes, and all the resources in these iframes, have fully loaded

Your solution works because the iframe it creates does not request the remote url until after the onload event. Having a set timeout on the first code, you would also get the page to load, then the request to the remote url to fire.

As to why the change of code broke the site, I can not seem to find any speed differences transferring between the two. Maybe it seemed faster because it was cached.

Revolution42
  • 191
  • 1
  • 5
  • It didn't 'seem faster'. The page literally ceases to load at the `document.write` in IE. Do you have documentation on the contents of external iframes delaying the onload event? – Yahel Jul 05 '11 at 15:29
  • I tried to create all the files locally and did not get the problem with the page not loading. As for external iframe you can find lots about it on google - http://stevesouders.com/efws/iframe-onload-blocking.php?type=img&t=1310045005 this has it all setup – Revolution42 Jul 07 '11 at 13:26
0

I don't know about the structure of your site, but normally the first script tag is in the <head>. An iframe in the <head> wouldn't be rendered. I'll wager if you did document.body.getElementsByTagName('script')[0] you would probably have similar issues to the one you described above.

cwallenpoole
  • 79,954
  • 26
  • 128
  • 166
  • This isn't correct. The version that you're talking about worked in IE without a problem; its the document.write version that didn't work, and only in IE, and only in the circumstance listed. I didn't state it above, but the `document.write` was in the `` tag, not the `` tag. The only reason `` generally doesn't display is that browser stylesheets set it to `display:none;`.A browser still loads the assets in the `` without a problem; it just doesn't physically display them. – Yahel Jul 06 '11 at 16:28
  • That was my point. I think the issue is in the rendering, not the loading. I suspect the addition of the iframe worked in this case because it was not displayed. I'm wondering what would happen if you added the iframe to somewhere that would display – cwallenpoole Jul 06 '11 at 16:33
  • Interesting. I misunderstood. I'll put together a test case using `document.body` as the injection point, and see what happens. – Yahel Jul 06 '11 at 16:41
0

Seems you're having a similar problem that I had several months back. The document.write triggers, and overwrites the page. Just use the iframe directly, and everything should be kosher.

Community
  • 1
  • 1
Glen Solsberry
  • 11,960
  • 15
  • 69
  • 94
  • 1
    Sorry, this isn't correct. I'm not trying to fix it -- I fixed the problem by using asynchronous injection. I'm trying to figure out why this particular case broke. Also, this isn't a case of `document.write` after `window.onload`; that would case the page to go white. This just stops the execution. – Yahel Jul 06 '11 at 16:25
0

I tried replicating your issue but couldn't on IE9.

Either I don't have the right test setup or it seems IE prior to IE 9 had some bug. Firefox had a simialr bug: https://bugzilla.mozilla.org/show_bug.cgi?id=293633

Maybe its a combination of unclosed iframe and something inside the page that's being rendered.

Mrchief
  • 75,126
  • 20
  • 142
  • 189
  • 1
    I don't have access to IE9, but, this test case shows the behavior exists in IE6, IE7, and IE8. http://sharedcount.com/test/ie-dw-if/old.html Does it show an `

    ` that says "Text after iframe"? If so, IE9 must not exhibit this same behavior.

    – Yahel Jul 11 '11 at 04:09