Where to put page-specific JavaScript

C

Christopher Nelson

I have a little menu system which essentially takes HTML like:

<div id='foo'></div>

and retrieves foo.shtml from the server and inserts it inside the
<div>. But sometimes I'd like foo.shtml to look like:

<script language='JavaScript'>
...do something AJAX-y
</script>
<div></div>

so that the script fills in the page. I've hacked together something
that inserts the foo.shtml into foo's div then does a
fooDiv.getElementsByTagName('script') and uses eval() on them and it
works most of the time in some brossers but it seems hackish and
somewhat dangerous and it doesn't work everywhere. Surely there's an
AJAX idiom (or even a DOM built-in) to execute scripts as parts of
pages load. Can someone enlighten me? TIA.
 
R

Randy Webb

Christopher Nelson said the following on 2/26/2007 11:38 AM:
I have a little menu system which essentially takes HTML like:

<div id='foo'></div>

and retrieves foo.shtml from the server and inserts it inside the
<div>. But sometimes I'd like foo.shtml to look like:

<script language='JavaScript'>
...do something AJAX-y
</script>
<div></div>

so that the script fills in the page. I've hacked together something
that inserts the foo.shtml into foo's div then does a
fooDiv.getElementsByTagName('script') and uses eval() on them and it
works most of the time in some brossers but it seems hackish and
somewhat dangerous and it doesn't work everywhere. Surely there's an
AJAX idiom (or even a DOM built-in) to execute scripts as parts of
pages load. Can someone enlighten me? TIA.

I am vaguely familiar with dynamically loading scripts, so, this thread
may help you. You will find more information and links about the problem
you have than you probably wanted to know :)

<URL:
http://groups.google.com.au/group/c...de+IE+Randy+Web&rnum=1&hl=en#3441a1cc21869a10>
 
C

Christopher Nelson

Christopher Nelson said the following on 2/26/2007 11:38 AM:








I am vaguely familiar with dynamically loading scripts, so, this thread
may help you. You will find more information and links about the problem
you have than you probably wanted to know :)

<URL:http://groups.google.com.au/group/comp.lang.javascript/browse_frm/thr...>

Thanks. I wish that thread ended, "And this is the best way to do
it..." :-( It seems that the most portable method of getting scripts
loaded is creating script elements and setting the scr property. At
http://members.aol.com/_ht_a/hikksnotathome/loadJSFile/ I found:

function cElement(fileName) {
var s = document.createElement('script');
s.src = fileName; //the name of the JS file.
document.getElementById('scriptDiv').appendChild(s);
}

With most of the corresponding column green. Is that an accurate
summary? For my situation, it seems that rather than having foo.shtml
with an embedded script tag, I should have foo.shtml with some
structure and a companion foo.js which gets loaded into that structure
with the code above. Or am I missing the boat somehow?
 
C

Christopher Nelson

Thanks. I wish that thread ended, "And this is the best way to do
it..." :-( It seems that the most portable method of getting scripts
loaded is creating script elements and setting the scr property. Athttp://members.aol.com/_ht_a/hikksnotathome/loadJSFile/I found:

function cElement(fileName) {
var s = document.createElement('script');
s.src = fileName; //the name of the JS file.
document.getElementById('scriptDiv').appendChild(s);
}

With most of the corresponding column green. Is that an accurate
summary? For my situation, it seems that rather than having foo.shtml
with an embedded script tag, I should have foo.shtml with some
structure and a companion foo.js which gets loaded into that structure
with the code above. Or am I missing the boat somehow?

It seems Michael Foster thinks this is the way to go:

http://www.cross-browser.com/x/lib/view.php?sym=xLoadScript
 
R

Randy Webb

Christopher Nelson said the following on 2/26/2007 4:31 PM:
Maybe I should post a reply to it and say "This is the best way to do
it" and refer to the function you listed below.

Yes, that is the best way of doing it.

Yes, that is a function I wrote about 3 or 4 years ago to load a .js
file on the fly. And to date, I have not come up with a better course to
do it.

Yes, a very accurate summary based on multiple people testing the page
for me to come up with those results.

That is probably the simplest way. You would load pageName.shtml and
pageName.js. Another option is to scan the innerHTML for SCRIPT
elements. If it has a source, load the .js file. If it has content, then
insert that content into the page.
It seems Michael Foster thinks this is the way to go:

http://www.cross-browser.com/x/lib/view.php?sym=xLoadScript

About the only difference between that function and mine (aside from the
feature tests) is that his is appending to the head element where mine
appends it in a DIV element that is solely for the purpose of appending
script elements (hence it's name of scriptDiv). The reason mine is
written that way is that when you load a file, load another, load
another, and so on, every single one of those files will remain a memory
load. Placing the script blocks in a div element makes it easier to
remove the. You can either cycle through the children of scriptDiv and
remove all the script blocks or simply set it's innerHTML to "" and the
previously loaded scripts are dumped and available for Garbage
Collection and free up the memory.

Thread that shows how to parse out script elements:

<URL:
http://groups.google.com/group/comp...y+webb+loadjsfile+getElementsByTagName&rnum=1>

Another on removing script elements:

<URL:
http://groups.google.com/group/comp...?lnk=gst&q=loadjsfile&rnum=8#2c55e50ba6f6fc66>

Right now Google Groups doesn't seem to be finding searches correctly
for the last 6 months or so. If you can find some threads with
loadJSFile in them a lot of them refer to other threads where some other
aspects of it were talked about. Another is the first thread where it
points to other threads (that I cant find via a Google Search).

This is another where it is discussed about injecting script blocks
(that are parsed out of the innerHTML block) and some of the associated
problems:

<URL:
http://groups.google.com/group/comp...?lnk=gst&q=loadjsfile&rnum=1#cf32cc0137dee3f2>
 
L

-Lost

Randy Webb said:
Christopher Nelson said the following on 2/26/2007 4:31 PM:

Maybe I should post a reply to it and say "This is the best way to do it" and refer to
the function you listed below.


Yes, that is the best way of doing it.


Yes, that is a function I wrote about 3 or 4 years ago to load a .js file on the fly.
And to date, I have not come up with a better course to do it.


Yes, a very accurate summary based on multiple people testing the page for me to come up
with those results.


That is probably the simplest way. You would load pageName.shtml and pageName.js.
Another option is to scan the innerHTML for SCRIPT elements. If it has a source, load
the .js file. If it has content, then insert that content into the page.


About the only difference between that function and mine (aside from the feature tests)
is that his is appending to the head element where mine appends it in a DIV element that
is solely for the purpose of appending script elements (hence it's name of scriptDiv).
The reason mine is written that way is that when you load a file, load another, load
another, and so on, every single one of those files will remain a memory load. Placing
the script blocks in a div element makes it easier to remove the. You can either cycle
through the children of scriptDiv and remove all the script blocks or simply set it's
innerHTML to "" and the previously loaded scripts are dumped and available for Garbage
Collection and free up the memory.

Hrmm... I assume you forgot the word script in "easier to remove the." So anyway, I was
wondering if you load however many scripts, you can at some point remove them and they can
still be available to the script?

(Admittedly, I did not test it. Forgive my laziness!)

Or we are just talking about getting rid of scripts that are no longer used?

-Lost
 
C

Christopher Nelson

...


About the only difference between that function and mine (aside from the
feature tests) is that his is appending to the head element where mine
appends it in a DIV element that is solely for the purpose of appending
script elements (hence it's name of scriptDiv). ...

He has another function which keeps track of scripts so that they
aren't loaded twice and it may -- I don't recall -- set things up so
they can be deleted. I don't really understand what difference it
makes -- in general, of specific to any browser -- whether the script
is in the head or body.
 
C

Christopher Nelson

...

That is probably the simplest way. You would load pageName.shtml and
pageName.js. Another option is to scan the innerHTML for SCRIPT
elements. If it has a source, load the .js file. If it has content, then
insert that content into the page.
...

I'm not clear on what innerHTML you're referring to. If I have a
foo.shtml:

<script src='foo.js'></script>
...some content here...

and bar.shtml:

<script type='text/javascript'>
...some code here...
</script>
...some content here...

I imagine a function which:

1. Gets the .shtml file from the server
2. Replaces the target div's innerHTML with the text returned
3. Uses div.getElementsByTagName('script') to find all the scripts
in the
new content
a. If the script had a src attribute, gets the js from the server
or
b. If the script had content, extracts its innerHTML
then
Insert the script text in scriptDiv or the head or something

This makes my suggested naming convention more flexible (foo.shtml can
use something other than or in addition to foo.js). My concern is
that IE7's been doing weird things as I play and I've read that
innerHTML isn't always supported.
 
R

Randy Webb

-Lost said the following on 2/27/2007 3:00 AM:
Hrmm... I assume you forgot the word script in "easier to remove the."

Yes, somehow it got removed before posting.
So anyway, I was wondering if you load however many scripts, you can at some point remove
them and they can still be available to the script?

They would be available until they get Garbage Collected.
(Admittedly, I did not test it. Forgive my laziness!)

Or we are just talking about getting rid of scripts that are no longer used?

Getting rid of scripts no longer being used at the time.
 
R

Randy Webb

Christopher Nelson said the following on 2/27/2007 8:10 AM:
He has another function which keeps track of scripts so that they
aren't loaded twice and it may -- I don't recall -- set things up so
they can be deleted.

I can see a possible use for not wanting to load files twice. If he has
it set up so that it doesn't load files twice then you can't delete them.
I don't really understand what difference it makes -- in general,
of specific to any browser -- whether the script is in the head or body.

It makes no difference as long as the order is there that they need to
be in. Meaning, a script block in the HEAD section can't try to call a
function in the BODY section until after it is loaded. But that is true
even if they are all in the body or all in the head section. The major
reason I use the scriptDiv approach is ease of removing script blocks.
Then again, all I load is pure data. The only thing in my .js files that
is not pure data is the very last line. It is a function call to call a
function in the main page to process the data in that file. It is the
only foolproof way I have come up with to know for sure the .js file has
loaded to that point.
 
R

Randy Webb

Christopher Nelson said the following on 2/27/2007 8:21 AM:
I'm not clear on what innerHTML you're referring to.

Me saying the innerHTML was wrong. What you have to do is retrieve all
the script blocks from the container and process them. That is another
benefit of the scriptDiv approach.

allScripts=document.getElementById('container').getElementsByTagName('script');

Now, you loop through allScripts (it is a collection) and handle each
script block. If it has a .src attribute you insert a .js file. If it
has text then you create an element and set its .text property.
If I have a foo.shtml:

<script src='foo.js'></script>
...some content here...

and bar.shtml:

<script type='text/javascript'>
...some code here...
</script>
...some content here...

I imagine a function which:

1. Gets the .shtml file from the server
2. Replaces the target div's innerHTML with the text returned
3. Uses div.getElementsByTagName('script') to find all the scripts
in the
new content
a. If the script had a src attribute, gets the js from the server
or
b. If the script had content, extracts its innerHTML
then
Insert the script text in scriptDiv or the head or something

That is precisely what you have to do. The benefit of the scriptDiv is
you can simply use removeChild and remove all the script blocks or set
scriptDiv's innerHTML to "" to remove any old content.
This makes my suggested naming convention more flexible (foo.shtml can
use something other than or in addition to foo.js). My concern is
that IE7's been doing weird things as I play and I've read that
innerHTML isn't always supported.

What kind of weird things with IE7? Other than createTextNode (which is
broken with script elements in all IE's) I am unaware of any problems
with IE7 and dynamically creating script elements.

As for innerHTML, what modern browser doesn't support it? I don't know
of one personally but there may be one.
 
C

Christopher Nelson

Christopher Nelson said the following on 2/27/2007 8:21 AM:
...

That is precisely what you have to do. The benefit of the scriptDiv is
you can simply use removeChild and remove all the script blocks or set
scriptDiv's innerHTML to "" to remove any old content.

OK. I'm still struggling to understand JavaScript scoping and
persistence rules. If I read a dynamic page's <script> element's
innerHTML, create a new script node, set that node's innerHTML, and
add that node as a child to scriptDiv, and then remove that script
node from scriptDiv, do functions defined in that script continue to
exist in the browser or do they only exist in the source text while
it's around?

As I sketched earlier, I imagine my pages having two types of scripts:
those that define functions to set up an environment and those that
are, essentially, onload handlers to invoke that environment. If I
have:

<script src='foo.js'></script>
<script language='JavaScript'>
aFooFunction();
</script>

I'd like to cache foo.js's definitions against the possibility of
reviewing this page later. I can do that with xSmartLoadScript()
which puts the functions in the header and only retrieves them the
first time. But I only want aFooFunction() executed once. If I
append a new script node to a scriptDiv every time I load the page,
I'll be creating lots of nodes that get executed once. If I want that
code to be executed once, I'm tempted to set up:

<script src='foo.js'></script>
<script language='JavaScript'>
aFooFunction();
</script>
<div id='fooScriptDiv'></div>

or something then when I find innerHTML in a script tag, create a new
script, add the innerHTML, append the new script node to fooScriptDiv
and immediately delete it. Or, at least, leave it there with the
knowledge that it'll get flushed when that dynamic content is replaced
by another file. My concern is that I don't know how soon I can
delete it. I've found that when I add a src-based script to the
document then immediately eval() code that invokes a function defined
in that script that the script fails with an error saying that the
function isn't defined. How can I know when the scr is processed?
What kind of weird things with IE7?

I don't have good notes but I know if was behaving strangely when I
was working with it a couple of days ago.
...
As for innerHTML, what modern browser doesn't support it? I don't know
of one personally but there may be one.

I may have been reading old notes or notes on old browsers.
 
C

Christopher Nelson

I imagine a function which:

1. Gets the .shtml file from the server
2. Replaces the target div's innerHTML with the text returned
3. Uses div.getElementsByTagName('script') to find all the scripts
in the new content
a. If the script had a src attribute, gets the js from the server
or
b. If the script had content, extracts its innerHTML
then
Insert the script text in scriptDiv or the head or something

This makes my suggested naming convention more flexible (foo.shtml can
use something other than or in addition to foo.js). My concern is
that IE7's been doing weird things as I play and I've read that
innerHTML isn't always supported.

In IE7, div.getElementsByTagName('script') is returning an empty array
even when I can clearly see <script> in the responseText:

var e = document.getElementById(id);
e.innerHTML = req.responseText;

alert('req.responseText:'+req.responseText);

var scripts = e.getElementsByTagName('script');
alert(scripts.length+' scripts found for '+id);
if (scripts.length > 0) {
for (var i = 0; i < scripts.length; ++i) {
var s = scripts;
if (s.innerHTML) {
var x = document.createElement('script');
x.innerHTML = s.innerHTML;
var h = document.getElementsByTagName('head');
h[0].appendChild(x);
h[0].removeChild(x);
}
if (s.src) {
xSmartLoadScript(s.src);
}
}
}

This works in Firefox and Opera. It appears that IE isn't updating
the DOM tree when I set the div's innerHTML.
 
R

Randy Webb

Christopher Nelson said the following on 2/28/2007 11:23 AM:
In IE7, div.getElementsByTagName('script') is returning an empty array
even when I can clearly see <script> in the responseText:

var e = document.getElementById(id);
e.innerHTML = req.responseText;

alert('req.responseText:'+req.responseText);

Does the very first item in the responseText happen to be a script
element? One thing I do know about IE7 is that if the very first element
is a script block then it gets fubar'ed. The solution I came up with was
to add an empty element prior to the string to get around it.

<URL:
http://groups.google.com/group/comp...ebb+br+script+dynamic&rnum=2#f79391b2eb1ebe1f>

Is the thread where it was talked about. In testing locally I can't
duplicate the problem unless I make the first element a script block.

If that is not the issue can you give a URL to a test page I can look at?
 
R

Randy Webb

Christopher Nelson said the following on 2/28/2007 9:36 AM:
OK. I'm still struggling to understand JavaScript scoping and
persistence rules. If I read a dynamic page's <script> element's
innerHTML, create a new script node, set that node's innerHTML, and
add that node as a child to scriptDiv, and then remove that script
node from scriptDiv, do functions defined in that script continue to
exist in the browser or do they only exist in the source text while
it's around?

Once you remove the code it is open to be Garbage Collected. Until it is
Garbaged it will remain. Otherwise it is gone as soon as you remove it.
As I sketched earlier, I imagine my pages having two types of scripts:
those that define functions to set up an environment and those that
are, essentially, onload handlers to invoke that environment. If I
have:

<script src='foo.js'></script>
<script language='JavaScript'>
aFooFunction();
</script>

I'd like to cache foo.js's definitions against the possibility of
reviewing this page later. I can do that with xSmartLoadScript()
which puts the functions in the header and only retrieves them the
first time. But I only want aFooFunction() executed once. If I
append a new script node to a scriptDiv every time I load the page,
I'll be creating lots of nodes that get executed once. If I want that
code to be executed once, I'm tempted to set up:

<script src='foo.js'></script>
<script language='JavaScript'>
aFooFunction();
</script>
<div id='fooScriptDiv'></div>

or something then when I find innerHTML in a script tag, create a new
script, add the innerHTML, append the new script node to fooScriptDiv
and immediately delete it. Or, at least, leave it there with the
knowledge that it'll get flushed when that dynamic content is replaced
by another file.

Yes, it will get flushed if you removeChild on the div element when you
want to load more:

Remove all child elements of scriptDiv
Add new children to scriptDiv
My concern is that I don't know how soon I can delete it.

I have never worried about it using the above order. If you empty out
the div before you append more then I have never run into an issue with
it. Even if the second set of scripts have a duplicate function name.
I've found that when I add a src-based script to the
document then immediately eval() code that invokes a function defined
in that script that the script fails with an error saying that the
function isn't defined. How can I know when the scr is processed?

The only way that I have found to reliable call a function in an
external file that is dynamically loaded is to put the function call at
the end of the file.

foo.js:

//lots of code here

someFunctionToExecute()

//end of foo.js
 
C

Christopher Nelson

Christopher Nelson said the following on 2/28/2007 11:23 AM:



Does the very first item in the responseText happen to be a script
element? One thing I do know about IE7 is that if the very first element
is a script block then it gets fubar'ed. The solution I came up with was
to add an empty element prior to the string to get around it.

<URL:http://groups.google.com/group/comp.lang.javascript/browse_thread/thr...>

Is the thread where it was talked about. In testing locally I can't
duplicate the problem unless I make the first element a script block.

If that is not the issue can you give a URL to a test page I can look at?

Yes, that's the issue. What a weird bug. I could have looked for
that for a decade or so without your clue. Thanks!

I've got:

var e = document.getElementById(id);
e.innerHTML = '<br>'+req.responseText;

var h = document.getElementsByTagName('head')[0];

var scripts = e.getElementsByTagName('script');
alert(scripts.length+' scripts found for '+id);
for (var i = 0; i < scripts.length; ++i) {
var s = scripts;
if (s.innerHTML) {
var x = document.createElement('script');
x.innerHTML = s.innerHTML;
h.appendChild(x);
h.removeChild(x);
}
if (s.src) {
xSmartLoadScript(s.src);
}
}

And IE7 gives me an "Unknown runtime error" on "x.innerHTML =
s.innerHTML". Firefox and Opera are happy with that code. :-(
 
C

Christopher Nelson

Christopher Nelson said the following on 2/28/2007 9:36 AM:
...

The only way that I have found to reliable call a function in an
external file that is dynamically loaded is to put the function call at
the end of the file.

foo.js:

//lots of code here

someFunctionToExecute()

//end of foo.js

My testing so far seems to support the idea that I can: 1) add a
script element with a src value which contains function definitions,
2) add a script element with innerHTML (with a prefixed <br>) which
invokes a function, 3) immediately remove the script element with the
innerHTML. I guess I'll see if that really works on all the browsers
I care about when I get IE to complaining about lines that are just
fine!
 
R

Randy Webb

Christopher Nelson said the following on 3/1/2007 9:30 AM:
Christopher Nelson said the following on 2/28/2007 11:23 AM:
Does the very first item in the responseText happen to be a script
element? One thing I do know about IE7 is that if the very first element
is a script block then it gets fubar'ed. The solution I came up with was
to add an empty element prior to the string to get around it.

<URL:http://groups.google.com/group/comp.lang.javascript/browse_thread/thr...>

Is the thread where it was talked about. In testing locally I can't
duplicate the problem unless I make the first element a script block.

If that is not the issue can you give a URL to a test page I can look at?

Yes, that's the issue. What a weird bug. I could have looked for
that for a decade or so without your clue. Thanks!

I've got:

var e = document.getElementById(id);
e.innerHTML = '<br>'+req.responseText;

var h = document.getElementsByTagName('head')[0];

var scripts = e.getElementsByTagName('script');
alert(scripts.length+' scripts found for '+id);
for (var i = 0; i < scripts.length; ++i) {
var s = scripts;
if (s.innerHTML) {
var x = document.createElement('script');
x.innerHTML = s.innerHTML;


x.text = s.innerHTML;
 
R

Randy Webb

Christopher Nelson said the following on 3/1/2007 9:39 AM:
My testing so far seems to support the idea that I can: 1) add a
script element with a src value which contains function definitions,
2) add a script element with innerHTML (with a prefixed <br>) which
invokes a function,

Don't set the script elements innerHTML, set it's .text property. See
the other reply about it.
 
V

VK

So anyway, I was
wondering if you load however many scripts, you can at some point remove them and they can
still be available to the script?

Yes.
<script> element is a "delivery shell" for text source being parsed.
After being successfully parsed you can remove that <script> element -
or all <script> elements all together from the page, it has no effect
on the execution context. Think of egg shells in the barn and happy
chickens on the backyard. What effect does it make on chikens if you
clean up the barn from the egg shells? Same for <script> elements and
the execution context.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,740
Latest member
JudsonFrie

Latest Threads

Top