Friday, October 16, 2009

IE8 Image is undefined

I was given a wonderful bug to work on today at work. Apparently we had numerous pages where a nice little javascript exception was being thrown, which in turn broke our pages. The issue was ONLY happening while using IE8, no issues with previous version of IE or Firefox/Safari/Chrome.

We use Omniture for site analytics and it was in their script that the issue was occuring. One problem we faced was the fact that their javascript is obfuscated so trying to read through their code was no fun. From what we could gather it looked as though they where using an 'Image' object with it's source set as a generated URL to report statistics.

After a bit of fiddler, javascript and code commenting we where able to narrow the issue down to this scenario

1. Within one window call 'window.open(...)' to open a new window
2. On the server, during the request for the new window, do a redirect to the same page
3. Once the page has loaded, after the redirect, you no longer have an 'Image' object
4. If you reload the page you now, magically, have an 'Image' object

(the redirect back to the same page was due to the original design of a few pages the application had. Basically a parameter is included in the query-string that notifies the server to reset a session variable, and the redirect goes back to the same page without the variable in the query-string)



According to some searching there are other scenarios where the 'Image' is undefined. Add-ons can cause this issue as well.

So.. What to do about this. Well like I stated in my previous post I tried to report the issue with MS, but unfortunately I could only post a comment on one of their forums. I also have an ASP.NET sample project that can consistently reproduce this issue, but I have nowhere to send it to. Maybe they don't like bug reports? I also couldn't find any useful information during my searching that could resolve this issue so it was down to some wonderful hacking...

My first attempt was to simply add this script to the top of our Omniture script

function Image() { }
Image.prototype = document.createElement('image')



That doesn't create exactly what you want though. But is interesting that you can get an instance of 'Image' by using the document.createElement('image').

For my next attempt I tried to see if I could get a reference from the parent window using

Image = window.parent.Image



But from the child window the parents Image was also null, even though it really wasn't when you actually tested it on the parent. Lovely I know.

But I was getting somewhere, since I didn't have what I wanted in the pop-up I was trying to get it from another window. So then the object/iframe tags came into mind. I wondered if the problem would still exist within an object/iframe tag within the broken page since it's DOM is different than the pop-ups.

I also found a post on Dean Edward's blog doing exactly what I needed so it saved me a bit of time/testing.

Sooooo here it is in all it's glory.

/*@cc_on
if
(typeof Image == 'undefined') {
    (function() {
        function createImage() {
            var iframe = document.createElement('iframe');
            iframe.style.display = 'none';
            document.body.appendChild(iframe);
            window.frames[window.frames.length - 1].document.write("<script>window.parent.Image = Image;<\/script>");
            //don't uncomment the line below as it causes IE8 to throw-up
            //document.body.removeChild(iframe);
        }
        var wl = window.onload;
        window.onload = function() { createImage(); if(!(!(wl))) { wl() } };
    })();
}
@*/



We wouldn't have to do the anonymous function and bind to the window.onload event, but in our case this was added to the top of our Omniture script file which is used as a script include at the top of our files (before the body tag) so the additional code was added so it would still work. Otherwise you could just add it to your page at the top/bottom wherever (as long as it's before any reference to 'Image')

What it does?
Basically we just create an iframe and add it to the DOM. Then we write the script string to the iframe which causes IE to execute it immediately. From within the iframe the 'Image' isn't broken/undefined so we set the parent's(broken window) Image to the iframes(working) Image. We ended up leaving the iframe in the page after the reference was set because IE would barf otherwise. This may be due to the reference on the parent no longer pointing to anything, but who knows. Oh yeah it's also wrapped in conditional compilation so that good browsers don't have to worry about anything.

6 comments:

Anonymous said...

Who uses IE8?! What a bunch o' losers!

BlackTigerX said...

sweet, I'll keep this around in case anyone finds this problem

jr said...

I just found someone with the same issue that said they resolved it by removing the call to 'focus' on their pop-ups... I'll have to test it out.. I'll post my results probably on Monday :)

Alchaemist said...

Really good solution, basically, you are getting a clean environment, and pushing the Image object from there. Long time ago I found one of the reasons why IE8 has problems with "Image", there might be others of course. I'll leave a link here
Regards!

Tim said...

Solution that works for me (put at top of file in a script tag):

if( typeof Image == 'undefined' )
{
Image = function() { return document.createElement('Image'); }
}

if( typeof Option == 'undefined' )
{
Option = function(text, value)
{
el = document.createElement('Option');
if(text)
el.text = text;
if(value)
el.value = value;
}
}

Tim said...

second function is missing "return el" at the bottom.