David Mark said the following on 12/5/2007 7:17 AM:
Very true as I don't believe that there is any way to compensate for a
document.write without writing your own parser.
It would certainly be a waste of time.
I don't think it would error out, it would never make it pasts the
feature testing. Although I don't even know that it would make it that
far in an AJAX application. "Fails silently without fallback" would be a
better description. If the innerHTML assignment was made first, before
any script insertion code, then it would insert the HTML, process the
scripts, and move on.
The test for innerHTML script execution is as simple as:
innerHTMLFailed = true;
window.onload=insertScript;
function insertScript(){
document.getElementById('scriptDiv').innerHTML='<script
type="text/javascript">innerHTMLFailed = false;<\/script>'
}
Aren't you missing the at the start of the string?
And then in the function you could have this:
if(innerHTMLFailed){
//code here
}
This is similar to what I did, but I don't test with a straight
innerHTML assignment as I use a wrapper for setting or appending HTML
that smooths out some of the issues with innerHTML. The wrapper uses
insertAdjacentHTML in a dummy DIV and then adds the nodes to the
target element. It seems it has identical issues with inline script,
including the need for the hack in IE.
With a conditional function, you could test for innerHTML script
execution and if it succeeded, you would simply insert the HTML and be
finished. In fact, I have changed my local version to insert the HTML
before the feature testing that is related to the script insertion.
I think the main reason I used a scriptDiv element was for testing more
than anything else. I could dump the innerHTML of scriptDiv to make sure
the script block got inserted if it didn't execute.
I tested for a script element at the head of the fragment and added
the , regardless of the browser.
That is a better way to go with it since it would only get added when it
was needed.
el.innerHTML = HTMLFragment;
var d =el.getElementsByTagName('script');
var t = d.length;
for (var x=0;x<t;x++)
{
var newScript = document.createElement('script');
newScript.type = "text/javascript";
if(d[x].src)
{
newScript.src = d[x].src;
}
That was added in the last version I posted as an afterthought.
else
{
if(isIE)
{
newScript.text = d[x].text;
}
This is where mine differs. I create an array of functions to try,
based on testing the text and canHaveChildren properties of a created
script element, then try them in turn. The first one that works is
the one used in the innerHTML wrapper. I try the text property first,
which may or may not be good idea. Does that property throw errors in
any agents that support createTextNode? If so, I will have to change
the testing order.
iCab, Safari2/Mac and some other fringe browsers error if you try to set
the .text property of a script element but using createTextNode works in
those two browsers. The main reason I used the isIE is because IE is the
only browser I am aware of that doesn't support createTextNode and the
idea was to use createTextNode and only use .text for IE.
I switched the order of testing to try createTextNode first.
Sure. Maybe the two together we can get something that is better than
either of them alone.
You won't be able to run this as it makes numerous references to
wrappers and an object defined in another script. The feature testing
logic looks like this:
methods = [];
s = createElement('script');
if (s) {
add = function(method, t) {
head.appendChild(s);
method(s, t);
head.removeChild(s);
};
if (doc.createTextNode && s.canHaveChildren ||
typeof(s.canHaveChildren) == 'undefined') {
push(methods, function(s, t) {
s.appendChild(doc.createTextNode(t));
});
}
if (typeof(s.text) == 'string') {
push(methods, function(s, t) {
s.text = t;
});
}
l = methods.length;
while (!api._testscriptinsertion && l--) {
add(methods[l], 'this.api._testscriptinsertion = true;');
}
if (api._testscriptinsertion) {
setElementScript = api.setElementScript = function(el, t) {
s = el;
while (s.firstChild) { s.removeChild(s.firstChild); }
add(methods[l], t);
}
addElementScript = api.addElementScript = function(s, t) {
s = el;
add(methods[l], t);
}
addScript = api.addScript = function(t) {
s = createElement('script');
add(methods[l], t);
s = null;
};
}
delete api._testscriptinsertion;
s = null;
}
So it doesn't actually do the innerHTML test first as it defines
general purpose script insertion/addition methods that can be used
without innerHTML.
After this it augments the HTML manipulation methods if they exist:
findAndAddScripts = function(el) {
var l, scripts = elementElementsByTagName(el, 'script');
l = scripts.length;
while (l--) {
if (scripts[l].text) { addScript(scripts[l].text); }
}
}
if (addElementHTML) {
addElementHTML(head, '<script type="text/
javascript">this.api._testaddhtmlscript = true;</script>');
if (!api._testaddhtmlscript) {
api.addElementHTML = function(el, html) {
addElementHTML(el, html);
findAndAddScripts(el);
};
}
}
There are two more clauses like this that have to run after the body
is parsed as they create and append a DIV to test the wrappers that
set inner/outerHTML. Worked for me, at least in the modern browsers I
use to test Ajax apps. Now that I switched the initial testing order,
it should work for iCab and those other odd Mac browsers that I don't
have here.