Skip navigation.

Ramblings

a test …

DOMContentLoaded gotcha with external stylesheets

, , , , , , ,

DOMContentLoaded is an event supposed to be thrown when a page's DOM is loaded, meaning that you don't need to wait for external images etc. to be loaded before running a script that expects a DOM to be in place. If you combine the initiation of your script with a JavaScript library like Interface you might get fatal consequences.


A user came in to the #webapps channel today complaining that Opera didn't show his accordion menu properly. In fact it only worked if the stylesheets he used were included inline, rather than referred to as external stylesheets.

After some digging around, including downloading the source of jQuery and Interface it turned out that the Accordion effect was trying to read CSS attributes and setting them again.

interface/accordion.js:
  jQuery(this)
          .css('height', jQuery(this).css('height'))
          .css('overflow', 'hidden');

Since this was started after DOMContentLoaded which in Opera fires before the CSS is loaded and applied, it made the elements keep the default values rather than those set in the CSS Stylesheet.

The solution to this specific problem was to avoid using DOMContentLoaded event when starting scripts that will eventually use CSS attributes, the startup was then wrapped in

window.onload

(MSIE),

window.addEventListener("load",function,false);

(Firefox) or

document.addEventListener("load",function,false);

DOM compliant browsers.

The moral I guess, is to watch out when using $(foo).ready in jQuery, and probably go for (on)load for anything that relies on CSS attributes being applied

Correct behaviour?
The question here is though. when should DOMContentLoaded really be fired. The scarce Mozilla documentation states:

DOMContentLoaded 

Fired on a Window object when a document's DOM content is finished loaded, but unlike "load", does not wait till all images are loaded. Used for example by GreaseMonkey to sneak in to alter pages before they are displayed. 

Does not appear to be dispatched to a Browser object (why?)

Which only explicitly mentions images, so I guess waiting for external styles is correct behaviour.

The HTML5 documentation at http://www.whatwg.org/specs/web-apps/current-work/#the-end doesn't seem to mention anything about wether or when style attributes should be appended to the elements in the documen before the DOMContentLoaded event is fired.

The name doesn't really indicate that one should wait for CSS -- Except when CSS is used to alter content of the DOM, like the content:-attribute -- Or possibly when using pseudo elements, so again, probably correct to also wait for external stylesheets.

However, I haven't peeked into the Mozilla Firefox code to see what it really waits for, and the original intention to have something else than onload that would fire before external data is loaded would suggest that DOMContentLoaded would fire before external CSS is loaded.

So, should Opera change behaviour, should Mozilla Firefox change behaviour, should the various events throughout a loading process be more fine-grained, and maybe throw events when various types of externally linked elements are being loaded? Suggestions?

Here's a test that says the Mozilla Firefox way is correct: http://people.opera.com/nicolasm/test/domcontentloaded.html

Pan or drag and scroll pages and imagesSound of terror

Comments

zenya 8. July 2007, 22:56

Before Opera upgraded style sheets were easier to use now they are not.

Also Opera would be used as IE not now.

I know changes are growing however, why ruin something good :frown:

nicomen 8. July 2007, 22:57

Hm, what do you mean?

zenya 8. July 2007, 22:59

I mean when you use a template before you can add your own CSS
now it's very difficult to make changes using their templates.

My online friend Diem use to do my blog theme for me since he is trained in grapic art and doing web pages and when I changed to the new template I have now he could not use the same CSS as it won't work with the new templates :frown:

Also before Opera would be reconized as IE now it isn't as far as I can see.

nicomen 8. July 2007, 23:05

So, the first issue is about my.opera.com and the CSS stylesheets we provide? Feel free to point out exactly how it is harder.

The second issue is quite easy to fix: Press right mouse button on any page and select Edit Site preferences. There you can tell Opera to identify as MSIE or Mozilla Firefox for any given site. Useful for some pages. But of course, if a page doesn't work in Opera because of browser sniffing, you shuold file a bug to the website.

Oh, btw. both issues are off-topic ;-P

zenya 8. July 2007, 23:11

yes I have used Opera to identify as IE and yet it won't work in somesites.

I just liked the option of not having sidebars and to have a full window for photos not the side window for my album photos showing.

I am sorry if I am interrupting you, that's why I wrote a pm not to take up your blog time..

BTW I won't post in the forums since some of the people in there have huge egos and lack of tollerance

nicomen 9. July 2007, 00:11

Ok, thanks for your feedback anyway. One reason it is nice to write in the forums though is that 1) you get a bigger public and someone else might be faster in answering and 2) when I file a bug report internally I can point to the forum entry from it so that other people on the team can easily see what the problem is.

This time I just copy-pasted your pm though, thanks again :wink:

hzr 9. July 2007, 16:50

Hey Nico.

Just a small comment. CSS does not alter the DOM in any way, even when using the content property.

Anonymous 9. July 2007, 17:40

graste writes:

Browsers (except Opera) won't render on-screen until all the stylesheets have loaded. I like Opera's interpretation as I see the page content earlier. Styles follow after they get loaded.

In Opera you can specify to "redraw after X seconds" in the advanced preference->browsing->loading. I'd rather see an unstyled table/page/whatever after 1 second than waiting for the content until all external stylesheets are loaded. As hzr pointed out, the DOM won't get altered by the CSS, so DOMContentLoaded is fired correctly by Opera. ;)

hallvors 10. July 2007, 15:36

My opinion: we're semantically correct but the real-world use case we're discussing shows that Mozilla's implementation can be more useful. So I'm for changing it. Another argument is that we now have DOM2Style support, so even though CSS doesn't change the DOM it is still a part of it.

Anonymous 10. July 2007, 16:15

graste writes:

Wouldn't that make Opera slower in response? Waiting for external content/stylesheets, that is.

nicomen 10. July 2007, 17:31

xErath 10. July 2007, 19:58

CSS does not alter the DOM in any way, even when using the content property.

But it affects layout, and a script which tries to read computed styles fails.
I think Opera fires the event when it should. When DOMContentLoaded fires, the browser may not have loaded any styles, external content, or reflown the document. If a script needs to wait for it, then it should wait for load. If the browser waits for styles then it'll introduce more lag.

_Grey_ 10. July 2007, 22:35

Well, using the content property, you're inserting (at least) a new text node into the document, maybe even an Image element, presuming content with image urls works like new Image().

And stylesheets are part of the DOM, as you can access them from it. So... there still is the question if browsers should wait for the stylesheets to be loaded, but that CSS doesn't chnage the DOM at all... I think that's too strong a statement...

HeroreV 11. July 2007, 04:45

For large pages on slow connections, it can take quite a while to load all the markup, so I dislike the idea of waiting around for the DOM to be fully constructed before doing anything. It's much better to check every element as it's constructed, and do what you can as soon as possible. I was on dial-up until a year ago, so I feel for the poor dial-up users. ;_;

But on topic, why not give developers an option? I don't see why browsers should only provide one or the other if it's not too difficult to implement both. Create something like a StylingApplied event that fires after the DOM has been constructed and CSS has been applied to everything. Options are good!

Anonymous 11. July 2007, 14:38

PeterS writes:

I think that Opera does fire the event at the correct time. I would imagine that if any other browser had the feature that Opera does (rendering the page before stylesheets are fully loaded) then they would run into the same issue.

However, I agree with hallvors: compatability concerns outweigh whatever beliefs I have about how I would have written the spec.

The other, more complex solution, would be to have opera fire the DOMContentLoaded event when it currently does, but then have the execution stall if it trys to access/write to a CSS attribute untill all CSS has been loaded.

Andrew Gregory 13. July 2007, 03:13

I think Opera should stay as it is, and fire DOMContentLoaded precisely when the DOM content has finished loading, regardless of any external resources.

If it's important to know when stylesheets have loaded, then separate "onload" type event handlers need to be registered. Images already support that, so I don't see why stylesheets couldn't either. Maybe they already do?

That gives much more precise and fine-grained control. If it is decided that DOMContentLoaded should wait for stylesheets to load, then that simply means that another event that does what DOMContentLoaded is supposed to do would be needed. Which would be quite perverse, IMO! p:

Anonymous 13. July 2007, 09:43

Anonymous writes:

I guess I agree that DOMContentLoaded should be exactly that, and that seems to be what HTML5 defines it as, but when running that test case with Mozilla Firefox which are the people actually inventing that event AFAIK, then external stylesheets are waited for, although it supposedly doesn't. *confused*

shoust 17. July 2007, 13:03

IMO an external resource is like an image, it shouldn't need for the resource to load to fire a DOMContentLoaded event.

AyushJ 23. July 2007, 17:58

I agree with SHoust

Some sites like Yahoo! etc. have very big stylesheets so waiting for CSS files before DCL event will only slow down things. You can already use load event if you want to make sure CSS files are loaded but what if you want to run script as soon as the DOM is loaded (in userjs etc.) ? Then there will be no solution :frown:

little_lemur 24. July 2007, 21:20

Nice one. . . :smile:

little_lemur 24. July 2007, 21:21

I love FireFox :D

liorean 28. July 2007, 09:44

I think the answer is that DOMContentLoaded should fire when the DOM has been built from the HTML source. Whether external CSS has finished loading or not shouldn't affect it in any way.

If people find a use case for finding when the CSSOM is finished from external sources, then browsers should try to provide a separate event for that. Say a DOMCSSLoaded event...



Why do I say that? Because I've found a need for DOM manipulation (particularly adding events on stuff) far more common than a need to work on the style sheets, and for some sites the style sheets can be unproportionally large compared to the HTML source.

How to use Quote function:

  1. Select some text
  2. Click on the Quote link

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

If you can't read the words, press the small reload icon.


Smilies

Download Opera, the fastest and most secure browser
December 2009
M T W T F S S
November 2009January 2010
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