From: David Mark on
On Dec 31, 6:22 pm, Hans-Georg Michna <hans-
georgNoEmailPle...(a)michna.com> wrote:
> It didn't quite work. document.readyState === "interactive"
> happens already when the DOM barely exists and is by no means
> complete.
>
> Has anybody seen any specification of whether "complete" happens
> before window.onload? Or do they happen at roughly the same
> time? If the latter, then I could simply use window.onload only.
>
> Currently I use this:
>
>   function waitForDomComplete() {
>     // Keep preexisting handler and append own handler:
>     var precedingOLHandler = window.onload;
>     // Use readyState if the browser has it:
>     if (document.readyState) {
>       if (document.readyState === "complete") {
>         doTheActualWork();
>         return;
>       }
>       var precedingORSCHandler = document.onreadystatechange;
>       document.onreadystatechange = function () {
>         if (precedingORSCHandler) precedingORSCHandler();
>         if (document.readyState === "complete") {
>           // Reset handlers, remove own handlers:
>           document.onreadystatechange = precedingORSCHandler;
>           window.onload = precedingOLHandler;
>           doTheActualWork();
>         }
>       }
>     }
>     // Use window.onload also, for all other browsers:
>     window.onload = function () {
>       if (precedingOLHandler) precedingOLHandler();
>       // Reset handlers, remove own handlers:
>       if (document.readyState)
>           document.onreadystatechange = precedingORSCHandler;
>       window.onload = null;
>       doTheActualWork();
>     }
>   }
>
> I think it is quite possible that I'm doing far too much, and a
> simpler piece of code would have nearly or entirely the same
> effect.
>

There's one big flaw I see: IE. Nothing in that will make IE call
back before the load event is fired.

From: Garrett Smith on
Hans-Georg Michna wrote:
> On Mon, 28 Dec 2009 16:58:50 -0800, Garrett Smith wrote:
>
>> Hans-Georg Michna wrote:
>
>>> Hi everybody; hope you all got over Xmas in good shape.
>
>> I finished in good shape.
>
> Ha! So now for switching years!
>

I'll go do some heavy deadlifts and snatches. Hows that sound?

>>> The following may be a frequently asked question, but I haven't
>>> seen any inspiring answer.
>>>
>>> Does anybody here have a good idea or some simple working code
>>> to solve the problem of having JavaScript code work on the DOM
>>> just after it is loaded?
>
>> What is the problem you want to solve that makes this seem an attractive
>> solution?
>
> Putting a table of contents into a web page, particularly inside
> a Content Management System, that is clickable and chapters
> reachable from the outside. The many known TOC scripts don't do
> all of this.
>

If the links are included in the markup, you won't need a script at all.
That can all be done on the server. What is the reason for not adding
the links to the markup?

When you say "chapters reachable from the outside", you mean that a
fragment identifier may linking to a chapter.

<div id="chapter1"> ... </div>

[snip code]

The example code, the callback for document.onreadystatechange changes
context from document to window.

document.readyState doesn't quite work so well in IE. "interactive"
means the element is loading and "complete" fires just before onload.

> Have not thoroughly tested this and may come up with
> improvements. Have thought a while about the closures on the old
> handlers, but the entire program should be garbage-collected
> anyway, after it is done. I'm not very experienced yet in these
> things. Anyway, this looks much cleaner than the Dean Edwards
> code, but will probably still work in many browsers, at least
> once Firefox 3.6 (with document.readyState) is gaining
> acceptance.
>

Given that existing implementations of IE will be around for quite some
time, detecting the presence of a document.readyState property won't
mean much of value for you.

you'll know it's done when readyState == "complete", but in IE,
readyState == "complete" is late and readyState "interactive" will give
you early results.

> It is interesting to notice how certain inventions like
> document.readyState or, for example, element.innerHTML gain
> universal acceptance without being formally standardized. The
> downside is that it is not enough to read the standard.
>
The closest thing to a document.readyState standard is in HTML 5
| document . readyState
|
| Returns "loading" while the Document is loading, and "complete" once
| it has loaded.
|
| The readystatechange event fires on the Document object when this
| value changes.

document.readyState is not reliable.
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Hans-Georg Michna on
On Thu, 31 Dec 2009 15:43:45 -0800 (PST), David Mark wrote:

>There's one big flaw I see: IE. Nothing in that will make IE call
>back before the load event is fired.

Not too bad. If IE doesn't, at least Firefox 3.6 will, or so I
hope.

I could scale this down and use only window.onload, which may be
the most sensible thing to do at the moment. Or I may just leave
the readyState stuff in there, as it doesn't do any harm and may
work increasingly better on new browsers. My impression is that
document.readyState is on a good way to becoming a standard, at
least a de facto one.

The big question is whether document.readyState === "complete"
comes before all the images and other things are loaded. If not,
then I don't gain anything over window.onload.

Hans-Georg
From: Garrett Smith on
Hans-Georg Michna wrote:
> On Thu, 31 Dec 2009 16:00:29 -0800, Garrett Smith wrote:
>
>> If the links are included in the markup, you won't need a script at all.
>> That can all be done on the server. What is the reason for not adding
>> the links to the markup?
>
> There's a number of reasons. One is that the text may live on a
> Content Management System and can be modified. Each modification
> would require conscientious adaptation of the TOC code, which
> not every CMS user may be up to. Another is that somebody puts a
> web page somewhere and wants a table of contents quickly without
> having to write more than a line of code or maybe four.
>
> I have thought for years about an XSLT solution. As all modern
> browsers contain an XSLT engine, it would be relatively easy to
> do for existing XHTML pages, merely by adding an xsl:stylesheet
> tag, but somehow I never got to it, even though XSLT is among my
> favorite programming languages.
>

Yes doing it on the server in XSLT would result in degradable links. For
a public site with users of any browser, and for search engines, that
can help.

The onload callback will interfere user interaction momentarily.

[...]


[...]

>> The example code, the callback for document.onreadystatechange changes
>> context from document to window.
>
> Oops, didn't even notice. What's the simplest way to do it
> better? How do you even know what the original context was?
>

The base object is document. When the `document.onreadystatechange` is
called, the base object determines the `this` value. If the base object
is an activation object or null, then null is used for [[Call]].

If the browser calls document.onreadystatechange, then it may provide a
different `this` value, as it does with some BODY event handlers, but
that doesn't happen here.

document.onreadystatechange = function(){
alert(this.nodeName);
};

elerts "#document"

When the function's internal [[Call]] property is invoked, if the `this`
value is not an object (null is not an object), then the `this` value is
the global object. Saving the value of document.onreaystatechange in an
identifier, the identifier gets the functional value. When the call
operator is used on that identifier, the base object is null and so the
this value is the global object.

var oldReadyHandler = document.onreadystatechange;

document.onreadystatechange = function(){
oldReadyHandler();
alert(this.nodeName);
};

"window"
"document"

(possibly multiple times).

|11.2.3 Function Calls
|
| The production CallExpression : MemberExpression Arguments is
| evaluated as follows:
|
| 1. Evaluate MemberExpression.
| 2. Evaluate Arguments, producing an internal list of argument values
| (see 11.2.4).
| 3. Call GetValue(Result(1)).
| 4. If Type(Result(3)) is not Object, throw a TypeError exception.
| 5. If Result(3) does not implement the internal [[Call]] method, throw
| a TypeError exception.
| 6. If Type(Result(1)) is Reference, Result(6) is GetBase( Result(1)).
| Otherwise, Result(6) is null.
| 7. If Result(6) is an activation object, Result(7) is null. Otherwise,
| Result(7) is the same as Result(6).
| 8. Call the [[Call]] method on Result(3), providing Result(7) as the
| this value and providing the list Result(2) as the argument values.
| 9. Return Result(8).
|
| The production CallExpression : CallExpression Arguments is evaluated
| in exactly the same manner, except that the contained CallExpression
| is evaluated in step 1.
|
| NOTE
| Result(8) will never be of type Reference if Result(3) is a native
| ECMAScript object. Whether calling a host object can return a value of
| type Reference is implementation-dependent.


The relevant step is step 7.

| 7. If Result(6) is an activation object, Result(7) is null. Otherwise,
| Result(7) is the same as Result(6).

[...]

>
>> The closest thing to a document.readyState standard is in HTML 5
>> | document . readyState
>> |
>> | Returns "loading" while the Document is loading, and "complete" once
>> | it has loaded.
>> |
>> | The readystatechange event fires on the Document object when this
>> | value changes.
>
> The question remains, what exactly "complete" means. With images
> or without?
>

Doesn't say and since the most recent browsers vary that's a good thing.
Otherwise they would be creating a dishonest specification.

>> document.readyState is not reliable.
>
> Since some browsers don't have it at all, I have to use
> window.onload as a fallback anyway.
>
^

The implementation of document.onreadystatechange is not boolean. The
point the callback fires is unspecified and implementations vary.

Really, I'd just go with window.onload. Another approach to consider is
adding the script to the bottom and just invoking it.

<body>
<div id="toc-container">...</div>

[cms contents]

<script type="text/javascript">fillToc();</script>
</body>
</html>

Aside from that, looking at your code, you did not terminate a few
assignment statements with semicolon. An assignment value of
functionExpression is no different:-
window.onload = function(){ };
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Hans-Georg Michna on
On Fri, 01 Jan 2010 15:36:22 -0800, Garrett Smith wrote:

>Hans-Georg Michna wrote:

>>> The example code, the callback for document.onreadystatechange changes
>>> context from document to window.

>> Oops, didn't even notice. What's the simplest way to do it
>> better? How do you even know what the original context was?

>When the function's internal [[Call]] property is invoked, if the `this`
>value is not an object (null is not an object), then the `this` value is
>the global object. Saving the value of document.onreaystatechange in an
>identifier, the identifier gets the functional value. When the call
>operator is used on that identifier, the base object is null and so the
>this value is the global object.
>
>var oldReadyHandler = document.onreadystatechange;

Thanks a lot for the excellent explanations!

So would it solve this inaccuracy to save the handler
temporarily in the document object, rather than in a variable,
such as:

document.onreadystatechangeOld = document.onreadystatechange;

Then later, when it is no longer needed, use:

delete document.onreadystatechangeOld;

By the way, null is an object, unlike undefined, which is not an
object. Perhaps the original designer of JavaScript had a hiccup
when he wrote this. (:-)

Still null is not very suitable, so �this� perhaps defaults to
the global object in case of null.

>Really, I'd just go with window.onload.

Done. Looks much cleaner and simpler too.

>Another approach to consider is adding the script to the bottom
>and just invoking it.

Yes, thought about that, but it gives the web designer who uses
the script another opportunity to do something wrong.

Also, in a Content Management System the script user may only be
able to get to the bottom of his content, not to the ultimate
bottom of the page code, which may cause problems in some cases.

So I'll stick to window.onload and hope that the DOM
specification designers one day see the light and give us what
we really need.

>Aside from that, looking at your code, you did not terminate a few
>assignment statements with semicolon. An assignment value of
>functionExpression is no different:-
>window.onload = function(){ };

Oops, that slipped through the cracks! Fixed. Thanks for finding
this!

Hans-Georg