So ive been using scriptaculous and prototype for a while, and ive now
been facing a huge problem for which i cant seem to find a solution.
Its pretty obscure, and i guess it all comes down to the document.write
method used in the scriptaculous loader class (scriptaculous.js), which
seems to invalidate the DOM tree until the page is loaded or soemthing
of this kind.
Ive created my own dialog and windows classes out of base and
prototype, inspired by lightbox.js. Instead of putting the whole code
in a single js file, i separated all classes. Now, i wanted to load
all these files without having to include them in all html files i do.
I checked out the code for the scriptaculous loader , which seemed to
do exactly what i wanted. So i duplicated the class, changed all names
to create a new class, loading the files i needed. I thought it worked
before i realized that when i was using both scriptaculous loader AND
my dialog class loader, depending on which was first, the second never
loaded any files.
Example :
<script type="text/javascript" src="scriptaculous.js"></script>
<script type="text/javascript" src="myloader.js"></script>
In this case, myloader.js never require the files its supposed to
include.
I searched for the problem for a while, and narrowed it to the require
function found in the loader. The first thing i noticed is that when
the scriptaculous loader loads its files, if i outputs the nodes
fetched by document.getElementsByTagName('script') in my class loader,
it doesnt include the myloader.js node. If i insert an alert in the
scriptaculous loader require function though, if i output the same
result as before, it WILL include the myloader.js node.
After some deliberation, weve concluded that when the scriptaculous
loader invoques document.write to include the script tag that will load
the javascript files, the browser seems to invalidate the current DOM
tree until we dont know actually when. Which does not prevent the
myloader.js class to be loaded, but the document.getElementsByTagName
are not complete since the DOM tree doesnt seem to have been updated.
If i include a alert message, it seems to allow the parser to
revalidate the DOM and then everything works as expected.
So my problem is kinda hard to explain, i hope some of you will
understand it and be able to help me a bit. I thought of using DOM
methods to include the script nodes, but as written in the comment,
Safari 2.0 will choke on that, and moreover, the script tags included
wont be included and read by the parser (i think.)
Any ideas on how i could require my files without resorting to
specifying a path in my code? I really like the automatic path
resolver that the scriptaculous loader uses.
Thanks for any help!
Dan
dfor...@gmail.com wrote:
> Its pretty obscure, and i guess it all comes down to the document.write
> method used in the scriptaculous loader class (scriptaculous.js),
It's not obscure, it's a known issue for a long time: Mozilla chokes
badly on gettingElementsByTagName while document is loading :(
Search their bugzilla, you'll find lotta related beasts ;-)
Anyway, as always, there are workarounds. All you'll have to do is to go
scanning the DOM (which _is_ properly updated) for the "script" tag. One
more hint: you don't have to collect all "scripts" and then filter for
yours, because the document is loading, so your script will always be
the last :))
Here's some code to start your app up :)
var MyApp = {
scripts: [
'/prototype/prototype',
'/scriptaculous/scriptaculous',
'myfoo',
'mybaz',
'mybar'
],
load: function() {
var path = this.getLastElementByTagName(
document, 'script').src.replace(/[^\/]+$/, '');
for (var i=0, len = this.scripts.length; i < len; i++)
this.require(path + this.scripts[i] + '.js');
},
require: function(src) {
document.write(
'<script type="text/javascript" src="' + src + '"></script>'
);
},
getLastElementByTagName: function(node, tag) {
if (!node) return null;
if (node.hasChildNodes()) {
var kids = node.childNodes, len = children.length;
for (var i = len - 1; i >= 0; i--) {
var element = this.getLastElementByTagName(kids[i], tag);
if (element != null) {
return element;
}
}
}
if (node.nodeType === 1 && node.tagName.toLowerCase() === tag) {
return node;
}
return null;
}
}
MyApp.load();
enjoy ;-)
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----
iD8DBQFFfMDytZHp/AYZiNkRAuR3AJ94Zj00VxJcS19HYXgDNmey0xnsPwCeLgLQ
nZqr/kOpv++jV2WMMS5l8bg=
=tWQE
-----END PGP SIGNATURE-----
Do you have a reference for that? I've searched both Firefox and
Mozilla bug lists for "getElementsByTagName" but found nothing
relevant.
--
Fred
The collection returned by getElementsByTagName is live, it continues
to be updated as the page loads. That may play a role in your issue
(or not...).
>
> So my problem is kinda hard to explain, i hope some of you will
> understand it and be able to help me a bit. I thought of using DOM
> methods to include the script nodes, but as written in the comment,
> Safari 2.0 will choke on that, and moreover, the script tags included
> wont be included and read by the parser (i think.)
I don't know why it says that, it's wrong.
The following modified scriptaculous require function works in Safari
2.0 (and other browsers):
require: function(libraryName) {
var scripts = document.getElementsByTagName('script');
var lastScript = scripts[scripts.length - 1];
var s = document.createElement('script');
s.type = 'text/javascript';
s.src = libraryName;
lastScript.parentNode.appendChild(s);
},
You could optimise that by getting the reference to
lastScript.parentNode (the head element) once and storing it as a
property of the Scriptaculous object.
> Any ideas on how i could require my files without resorting to
> specifying a path in my code? I really like the automatic path
> resolver that the scriptaculous loader uses.
The above does that. The whole thing seems very convoluted, a for loop
to add the script elements seems much simpler whether DOM or
document.write() is used.
Nearly all the unit tests are a disaster in Opera 9 for OS X.
--
Fred
IIRC, that's a bug in earlier Safari 2.0 releases (~around Mac OS X
10.4.0/10.4.1 or so).
-Thomas
I had a very similar or maybe identical problem and reported it to the
Scriptaculous trac. It was changed to "won't fix" even though I found
and reported a way to make it so the Scriptaculous script idea will
work twice. Perhaps there is an even better solution than the one I
found. I quickly abandoned this loading technique.
http://dev.rubyonrails.org/ticket/4335
This ticket could have been left open showing a desire to improve but
was closed for unknown reasons to me. I was very disappointed with the
general attitude that leaving broken code in place was ok in
Scriptaculous and hence in Rails. It is unfortunate that the
Scriptaculous file is still setting an example of broken code and
other people are copying it just like I tried.
I now don't think a file like the Scriptaculous loader is a very good
idea anyway. It just causes more client-server talk with no real
benefit.
Peter
-------
http://forkjavascript.org
I tested in OS X 10.4.8. Subsequently I tested in OS X 10.2.8 (Safari
1.0.3) - loading using DOM fails, as do the vast majority of
Scriptaculous unit tests.
--
Fred
Fred wrote:
> Do you have a reference for that? I've searched both Firefox and
> Mozilla bug lists for "getElementsByTagName" but found nothing
> relevant.
I remember I had a long conversation some time ago on this issue, and
lots of bugzilla tickets came up to "support" our concern. Luckily, we
have that conversation on record, so here are some tickets that may be
of interest: 276037, 331174, 18333 (there are 17 more in fact, but as
you will see from 18333, the problem goes down way too deep to be able
to follow it easily) ;-)
I see there were lots of tests cases involved, but no URL is still
alive. If you are interested, I'll try reviving some of them and maybe
forge something more appropriate as a "proof of concept" for us.
Yet, does somebody else care about this issue? As it seems,
dfor...@gmail.com got his issue solved by my previous message and - as
Peter Michaux told us - script.aculo.us does give a shit about it, as
"scriptaculous.js file is only provided for convenience".
cheers
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----
iD8DBQFFffaltZHp/AYZiNkRAmXKAKDAf/2lIqwSr21XxytPBDjfuqBgKwCfboez
a95iKAS28lb5Q/0Qe/WMIHE=
=raif
-----END PGP SIGNATURE-----
Thanks, I needed to search "Core" rather than a product name.
> I see there were lots of tests cases involved, but no URL is still
> alive. If you are interested, I'll try reviving some of them and maybe
> forge something more appropriate as a "proof of concept" for us.
The test page for 276037 still works.
> Yet, does somebody else care about this issue?
Yes, it's applicable to other situations.
--
Fred
Fred wrote:
>> I see there were lots of tests cases involved, but no URL is still
>> alive. If you are interested, I'll try reviving some of them and maybe
>> forge something more appropriate as a "proof of concept" for us.
> The test page for 276037 still works.
Yes, but that test isn't so easy to choke ;-)
So, here it is:
http://gfx.neohub.com/t/getElementsByTagNameTest.html
And a sample loader (seeing that this already got another request on a
newer thread):
http://gfx.neohub.com/t/loader-test.html
cheers
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----
iD8DBQFFfwqvtZHp/AYZiNkRAuBeAKCF00hXQj6l9PiuiP2EH+cQE0E20wCeMAAa
Z9kibJPrqkRgV2BHbrblBYk=
=KHlK
-----END PGP SIGNATURE-----
Do you mean it's too simple? It demonstrates the issue, which is all it
needs to do.
> So, here it is:
> http://gfx.neohub.com/t/getElementsByTagNameTest.html
>
> And a sample loader (seeing that this already got another request on a
> newer thread):
> http://gfx.neohub.com/t/loader-test.html
Why is getLastElementByTagName so complicated? I don't understand the
purpose of recursion over descendant nodes rather than just getting the
last member from the collection returned by getElementsByTagName. The
difference in performance will be huge.
As for your loader function, you seem to make provision within your
scripts array to include different paths, so why not do that and
dispense with getting the path of the current script element
altogether? The whole function then becomes a simple iteration over
the scripts array with document.write to add them. ;-)
<script type="text/javascript" src="load.js"></script>
Where load.js is:
(function(){
var scripts = ['foo.js', 'bar.js'];
for (var i=0, len=scripts.length; i<len; i++){
document.write('<script type="text/javascript" src="'
+ scripts[i] + '"><\/script>');
}
})();
--
Fred
Fred wrote:
>>> The test page for 276037 still works.
>> Yes, but that test isn't so easy to choke ;-)
> Do you mean it's too simple? It demonstrates the issue, which is all it
> needs to do.
You just proved my point, it doesn't (see below) ;-)
> Why is getLastElementByTagName so complicated? I don't understand the
> purpose of recursion over descendant nodes rather than just getting the
> last member from the collection returned by getElementsByTagName.
Because of the subject of this topic - getElementsByTagName fails (at
least on Gecko) while document is loading. Run my test on Gecko and you
will see lots of red labels (errors). Please view source :)
> As for your loader function, you seem to make provision within your
> scripts array to include different paths, so why not do that and
> dispense with getting the path of the current script element
> altogether? The whole function then becomes a simple iteration over
> the scripts array with document.write to add them. ;-)
Wrong, as this means scripts have paths relative to the current
_document_ (vs current _script_).
e.g.: Page "/myapp/index.html" includes a "js/load.js". Using your
simplification...
> <script type="text/javascript" src="load.js"></script>
<script type="text/javascript" src="js/load.js"></script>
> Where load.js is:
>
> (function(){
> var scripts = ['foo.js', 'bar.js'];
> for (var i=0, len=scripts.length; i<len; i++){
> document.write('<script type="text/javascript" src="'
> + scripts[i] + '"><\/script>');
> }
> })();
<script type="text/javascript" src="foo.js"></script>
^ absolute path would be "/myapp/foo.js", which obviously is incorrect,
as "myapp" modules are stored in "/myapp/js/".
Anyway, that loader is just a lazy example, to prove that it can load
correctly even scriptaculous (with is own buggy loading procedure).
Obviously, it can be improved to support even more complex paths, like
"../lib/", etc.
cheers
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----
iD8DBQFFf6rKtZHp/AYZiNkRAhrsAJ0cJqJYyV3oaiyffEKzmRXJ1bl3NgCfSSFS
utCAkjFhjuV1Hd17v6BB0hs=
=ALmr
-----END PGP SIGNATURE-----
You proved my point: I don't understand what you mean by "choke".
>
> > Why is getLastElementByTagName so complicated? I don't understand the
> > purpose of recursion over descendant nodes rather than just getting the
> > last member from the collection returned by getElementsByTagName.
> Because of the subject of this topic - getElementsByTagName fails (at
> least on Gecko) while document is loading. Run my test on Gecko and you
> will see lots of red labels (errors). Please view source :)
It only fails in one single scenario: if you use document.write to add
elements from within the same script element that is trying to use
getElementsByTagName, *and* you are trying to get a reference to those
elements.
For a load script, there is no reason to use getElemensByTagName
multiple times: you only have to get the path once per loader script
element - the path of the current script isn't going to change.
Therefore you can use getElemetsByTagName quite happily in a script
loader.
>
> > As for your loader function, you seem to make provision within your
> > scripts array to include different paths, so why not do that and
> > dispense with getting the path of the current script element
> > altogether? The whole function then becomes a simple iteration over
> > the scripts array with document.write to add them. ;-)
> Wrong, as this means scripts have paths relative to the current
> _document_ (vs current _script_).
Gimme a break. You've got to get the path from somewhere, you can
either get it from the current script element or a parameter. You can
make them relative to either path, or absolute, it's up to you.
The use of getElementsByTagName doesn't have any impact on that other
than being one of the tools (probably the best) you can use to get the
path of the current script element.
--
Fred
Fred wrote:
> You proved my point: I don't understand what you mean by "choke".
My apologies, bad-bad wording indeed :)
After reading all the new posts in these two duplicate threads I thought
it's time to end it (AKA ignore them) as there's nothing new to be said
on this issue and people are just getting inflammatory.
Anyway, in order to properly drop myself out of this discussion, I'll
say it again, as people seem to have a very wrong impression about my
posts in this thread: my post was neither "advocacy", nor any kind of
project "in the works", it was merely a presentation of some
_workaround_ for the problem raised by the starter of this thread.
To summarize, the problem was that getElementsByTagName() cannot be
reliably used on Gecko to get the list of elements in document's <head>
while it is still "loading". (Please read Fred's posts to get a higher
resolution picture).
The workaround proposed by Martin Honnen (
https://bugzilla.mozilla.org/show_bug.cgi?id=276037#c12 ) was to scan
the partially loaded DOM. The performance drop should be quite light
given the fact that "the DOM" is merely composed by a few elements (the
ones already loaded in <head>) at the time this method is executed.
As a side note, saying that getElementsByTagName can in fact be used
_one time only_ it's not a valid argument, as one exception is already
too much (as in "all tests successful" vs "failed 1 of99999 tests").
merry coding ;-)
- --
Marius Feraru
-----BEGIN PGP SIGNATURE-----
iD8DBQFFgQ2gtZHp/AYZiNkRAjfzAJ9FtNzUW+/s+ZGG+8iiggxVMQ5hvACfaUYO
i+0uSS2GqZ0Kzo6R6cv+MJg=
=8yWs
-----END PGP SIGNATURE-----
No problem.
> Anyway, in order to properly drop myself out of this discussion, I'll
> say it again, as people seem to have a very wrong impression about my
> posts in this thread: my post was neither "advocacy", nor any kind of
> project "in the works", it was merely a presentation of some
> _workaround_ for the problem raised by the starter of this thread.
Understood - code posted to a news group should only be for examples,
if only to cut the necessary clutter of full-strength stuff. It's also
impossible to tell where a thread will end up, that's the beauty of
Usenet. ;-)
Public forms are important for expressing opinions and getting
criticism - take it with a grain of salt (or sugar, as the mood takes
you).
[...]
> To summarize, the problem was that getElementsByTagName() cannot be
> reliably used on Gecko to get the list of elements in document's <head>
> while it is still "loading". (Please read Fred's posts to get a higher
> resolution picture).
Cheers. I've learnt a thing or two about Gecko, the scriptaculous
loader function got a kick, Peter's alternative got poked and prodded,
hopefully his lodging of a better version through a ticket will deliver
users a better script.
Everyone's a winner! :-)
[...]
> merry coding ;-)
Merry Christmas.
--
Fred
Just to say thanks for all the discussions and solutions proposed, i
didnt have time to try them out on my problem but im 100% confident
that it will solve it.
This group is awesome, i didnt think id have an answer that fast!
Thanks again and happy holidays!
Dan