Archive for February 2008

 
 

Issues with Opera, Prototype, and dom:loaded

I’ve been working on a small JavaScript library that is built using Prototype, and discovered a small quirk that caused Opera and Safari to respond differently than IE and Firefox when using the dom:loaded event with the document.observe function.

According to the Prototype documentation dom:loaded will “fire immediately after the HTML document is fully loaded, but before images on the page are fully loaded.” My script manipulates a set of images that are on the page, and I needed to process each image before the page loaded. If I waited until the document had fully loaded, then a flicker would occur in a couple of browsers. Unfortunately, it wasn’t happening in every browser so this made it difficult to diagnose.

Initially, my approach was to first intercept the images, hide them (save for the first image), and instantiate my object all before the DOM had finished actually loading the images. This is basically what I was doing:

document.observe(’dom:loaded’, function() {

// error checking, reading images into an array here
images.each(function(i) {

if(i != images.first()) {

i.hide();

}

});

// apply some specific margin styles here

});

The end result worked well - it behaved exactly as I had anticipated across IE6, IE7, Safari, Opera, and Firefox but the problem didn’t occur until I uploaded everything to a web server and began running tests across these same browsers. That’s when I found an ugly bug: Opera appeared to be completely ignoring all of the initial processing. Safari occasionally demonstrated the same exact error, but it was inconsistent - sometimes it worked, sometimes it didn’t.

Rather than try to hack the code to get it to bend just enough for Opera, I wanted to investigate it and figure out exactly what was going wrong. Sadly, Opera doesn’t have a nice DOM debugging console (at least not that I found), so I had to resort to creating some test cases to pinpoint the issue.

First, the reason that the error didn’t show its face until the script was running on a web server is because the images were stored locally so there was close to no time required for the images to load - on a web server, that’s obviously different.

Secondly, regarding the script, I’m not 100% positive that this is the problem, but it’s as close as I could come based on the results of my tests. What I found is this:

Earlier, I mentioned that the dom:loaded event is triggered prior to when the images are loaded, right? Well, I was initially trying to access the image.height property during this processing. Whereas the other browsers were already parsing each image’s properties, Opera was not. From what I gather, the other browsers are loading up the properties of each image prior to actually displaying them, and Opera is delaying all of this until the dom:loaded event has completed. Basically, this makes it impossible to process any of the images until they are visible on the screen.

After discovering this, I made some changes to the code. I was somewhat reluctant to do this at first because I didn’t wanna sacrifice some of the slight performance issues that would come as a result of the fix; however, the cost of flexibility outweighs the minimal performance decrease, in my opinion. Here’s what I did:

document.observe(’dom:loaded’, function() {

// error checking, reading images into an array here
images.each(function(i) {

if(i != images.first()) {

i.hide();

}

});

});
Event.observe(window, ‘load’, function(evt) {

// apply some specific styles to the images here

});

Ultimately, this still allows me to hide all of the images prior to them being displayed so none of the actual visuals are affected, but I delay the image processing until the images are actually loaded by the browser. This proved to completely fix the bug in Opera and also stopped the inconsistency issues in Safari.

As I mentioned before, I’m not 100% positive that the results of my testing are correct, but it seems to be close based on the results. Additionally, this fix ended up addressing the cross-browser quirks so I’m content with the solution. If any of you are familiar with the issue that I was having, or have had any similar experience, leave me a comment.

Later, I’ll go more in depth on this library that I’ve been working on, but I’m not quite done with it so there may be more issues to discus.

Until then,
Tom

IE & The Ajaxian Injection Rejection Problem

Recently, I’ve been working on an application that, upon the successful completion of an AJAX request, injects some HTML into the current DOM. While working on this project, I kept getting an “Unknown runtime error” message in IE6 and IE7 all the while Firefox was responding the request by injecting the HTML exactly as I wanted. Before you go hating on IE and loving on Firefox (which I was guilty of doing), read on - IE was actually doing the right thing, just in a poor way.

What was wrong?
Here’s a snippet of my JavaScript as well as a comment as to which line was causing the problem:

if(response.get_responseAvailable()) {


var div =
document.getElementById(’divJobInformation’);
// here’s the problem…
div.innerHTML = response.get_responseData();

} else {

} // end if/else

Basically, what’s happening here is that, permitting the response has completed successfully, it brings back a chunk of HTML that I want to place in my current page. Unfortunately, it was halting on both versions of IE and displaying this error:

Just to make sure I had covered all my bases, I double checked my Firebug console and it wasn’t reporting any error at all. I ended up trying out a couple of test cases to see if I could pinpoint the problem. First, I tried to simply inject a string literal into the DOM. It worked. Next, I echoed the response data into a JavaScript alert box. Everything was displaying fine.

So what did you do?
After coming up empty there, I ended up going through some of the application code that was responsible for handling some pre-processing of the page into which I was trying to inject this data. Now, I’m someone that tries to comply with W3C standards in every way possible, but I failed here: What I ended up finding was that some of the data that was coming back from the AJAX request contained a block level element and it was being injected into an inline element. That’s bad style, and I should have caught it sooner, but I was knee deep in code and I failed to catch it. I couldn’t see the forest through the trees, so to speak.


Did you bother verifying this?

Yep. After I got the rest of the application working, I wanted to make sure that I fully understood what the problem was, and to verify that it was repeatable in all (or almost all) cases. I ended up making a quick sandbox page that would allow me to dynamically choose a single containing element and then trying to inject a collection of other elements into it. One example would be trying to place a header element in a paragraph element. IE threw an error for every one of the tests I ran, Firefox let it slide.

But there’s a catch: If you were to place a header element inside of a paragraph element in your HTML source and then load it up in a browser, it works fine - IE will actually parse it and display it with no errors. If, however, you attempt to inject a header element into a paragraph element after the DOM has loaded, it will throw an error every time.

Surprisingly enough, IE (instead of Firefox) is actually doing the right thing by rejecting this attempt to insert response data in an ill-formed manner. Unfortunately, the error message that it returns gives you almost no direction into which way to really start debugging the script. Maybe I should have caught this from the get go, but judging by a quick Google search, it seems as if others have experienced similar pains.

If you’ve got any thoughts, comments, questions, or more experience with this stuff, leave me a comment!
Tom