Jorge said:
And a video here: Steve Souders: "Life's Too Short - Write Fast Code
(part 2)", "Advanced script loading: six ways to load scripts without
blocking the page" (starting @10m40s)
DON'T MISS IT: John Resig's über-cool "degrading script pattern",
starting @22m15s.
Thanks for that link. That is horrible advice and should be avoided.
It was discussed on John Resig's website[0].
I wonder why jewels like these don't make its way into the resources
FAQ.
Good point. It is worth mentioning that there is a lot of poor advice
provided by experts that should not be followed. The web is horribly
broken. This video serves as an excellent example of an "industry
expert" providing up horrible advice. It should be recommended against
and the section on "John Resig's degrading script tags", which Steve
calls "really really awesome", serves as a fine example of why.
<FAQENTRY>
Q: I saw a video online. Why wasn't it in the FAQ?
A: Video presentations often contain advice that is either
incorrect or inadvisable. It possible, though unlikely, that the video
has not been reviewed and contains accurate and valuable information.
Before posting a request for review, please search the archives for
discussion about the video.
</FAQENTRY>
Steve:
| It turns out, this is really really awesome, but it actually doesn't
| work in any browser. There's no browser that works this way to like
| pay attention to the code in the script blocks if there's a source
| attribute being used.
|
| But it turns out its really easy across all browsers to add that.
| And the way we add that is.. Oh, and this is nice because it's
| cleaner, there's only one script tag instead of two script blocks,
| it's clearer, its very clear that this script code has a dependency
| on menu.js, um, and it's safer. If the external script fails, then,
| ah, the inline code won't be scalled.
|
| But, since it doesn't work in any browser, you have to do a little
| work. And it's not that much work and it's not that complicated, so
| at the end of menu.js, so that's why I renamed it to
| "menu-degrading.js", so at the end of "menu-degrading.js", I basically
| just do this loop:
|
| I get all the script elements, I loop through them, until I find the
| script whose name is "menu jay", "menu-degrading.js" and then I look
| at its innerHMTL property. And that's basically going to be, innerHTML
| is going to be this code, right here (points to example), and I just
| eval it.
None of the three reasons is true. All are blatantly false and in being
false, they serve as reasons for eschewing this technique.
Because a SCRIPT element is not allowed to have HTML, expecting a script
tag to have its text available as innerHTML seems risky. For example:
javascript: alert( document.createElement("script").canHaveHTML)
The "inline code" here is defined as fallback content in HTML 4.01[1].
That means it is only displayed if the script does *not* load. That is
the exact opposite of what this technique requires. Here, Steve is
expecting the fallback content of a script tag to be executed only when
the src is loaded. This works exactly the opposite of the way the HTML
4.01 standard states and this was clearly pointed out on John Resig's
blog[3][4]. It is unreasonable for Steve to have ignored this criticism,
yet he continued to advocate it *after* that fact was pointed out. Steve
Souders is arrogant and not amenable to criticism.
The script's innerHTML is then passed to eval.
The file name changed it's not "menu.js", but now "menu-degrading.js",
but notice how Steve gets that wrong a couple of times. That right there
is evidence that the "degrading" part is unrelated to the original code.
The inline code at the end of menu-degrading.js loops through elements
in the document. It uses all global variables, including a global loop
counter |i|. The eval method is called in global context.
Looping through the DOM while the page loads is a strategy that hurts
performance.
AISB, eval is called in global context. Steve did not mention this, and
the code is so naive that I doubt he is even aware of it, but the code
must exist in global context, or use a separate sandbox function. This
has to do with eval using the scope of the calling context, variable
object, and the this value. If eval is called within a function, then it
will use that function's [[Scope]], variable object, and |this| value.
This can complicate the situation by identifiers being resolved to that
scope chain.
Example:
function doEval(s) {
var i = 10;
eval(s);
alert(i);
}
doEval("i = 20;");
Result:
elerts "20";
I've used the identifier |i| on purpose because it is a common mistake
to forget |var| in initializing a loop variable.
Such issue might not happen on a simple first test. However, if put into
production, it could happen at a later time with different data and
different identifiers. It could potentially causing bugs that might not
arise immediately. If and when they happen in production, it would
severly compromise the security of an application. Inline code often
makes use variables from the server, e.g.:- <%= serverOutput %>, so it
might be a problem that could go unnoticed until, well, it became a
horrible security problem.
Steve mentions in that video that "Doug" remained silent when he
mentioned eval.
Another problem with eval is that when an EvalError occurs, the browser
may not provide information about what was being eval'd that caused the
problem. Instead, the browser will state that eval(s) caused a problem.
Since eval can't be stepped into, it is harder to debug. Indeed, this
is a problem in Dojo that I noticed way back in Dojo 0.4.
Back to Steve's example, that the file "menuteir.js" now has to know
something about the application's performance tricks. That is awful
design and a flagrant violation of SRP. Steve says it is cleaner and
clearer. That is untrue. It is less clear, more complicated because it
requires more code inside menuteir.js to execute the inline script
content. The menuteir.js should not be have knowledge of the performance
tricks used to include it. It severely complicates things; it does not
make things simpler, nor clearer.
It does not make things "safer" either, as I have already explained the
problems with eval.
| If the external script fails, then the inline code won't be called.
That may be true in buggy implementations, but according to the HTML
4.01 specification, the exact opposite is designed to happen. Code
should not rely on specification violations. Utilizing proprietary
(MSIE) extensions is one thing, but expecting every browser to go
against the spec is a very *unsafe* design decision.
Not cleaner, nor clearer, nor safer.
The technique requires implementations to violate the HTML 4 standard.
(According to Steve, this is "all browsers"). It complicates the design
of the code, making it harder to tune and increase likelihood of errors.
For browsers that behave the way Steve wants, Steve's approach impacts
performance by looping through the DOM, and, by using eval, introduces
fragility into the code by eval.
It is really a very poor idea. As Randy said, KISS.
Steve Souders is ignoring valid criticism, disregarding public
standards, and throwing caution to the wind.
I see that Randy and Thomas have their own thoughts on this. The
criticism I provide can tends to annoy the "experts" quite a bit. See
the whatwg list for details. Good to see that I am not alone here.
The part about "depends" in 28:00 is where steve talks about declarative
chaining of script loads. It is another very poor design idea, and it
prompted my proposal for "depends" on script tags on whatwg. That design
idea follows the "I'm Done" paradigm, not "Do This". For more
information on "I'm Done", see H.S. Lahman's explanations on comp.object.
The "Managed XHR" solution also requires eval, and has all the
complications associated with that, just as in Dojo.
Technique 4 Script onload aka "The Best One", shows code that modifies
Host objects with expandos. For reasons that have been discussed here
many times, code which modifies host objects is error prone. According
to Steve, if you use both onload and onreadystatechange, it will work in
"all browsers". Again, this relies on the approach of "Do This" instead
of "I'm Done".
I do agree that this deserves mention in the FAQ. If Steve's book is
going to be included, it ought to mention that his application design
advice is considerably harmful.
Garrett
[0]
http://ejohn.org/blog/degrading-script-tags#postcomment
[1]
http://www.w3.org/TR/html401/interact/scripts.html#h-18.2.1
[3]
http://ejohn.org/blog/degrading-script-tags#comment-319983
[4]
http://ejohn.org/blog/degrading-script-tags#comment-320005