Safari scope problem with dynodes

D

dd

Hi,

I've discovered a scenario where Safari 1.3 (I need to make my stuff
compliant with 1.3+) gets confused about the scope of local variables
WITHIN functions that were created in dynamic script blocks. I've made
this example where function def has a local i variable in a loop, and
it calls function abc which also has a local i variable in a loop. What
happens is that Safari is not respecting the scope and is allowing the
called function to corrupt a local variable in the parent function

Here's the whole test page including html tags. If you try it you'll
see that IE and Gecko both produce the output "in abc" twice, because
the def function correctly gets to call abc twice. On Safari, i gets
corrupted, and abc only gets called once... Any ideas what I can do to
prevent this?

<html><body>
<script language="javascript">
function abc() {
for(var i=0; i<1; i++)
document.getElementById("dd").innerHTML+="in abc<br>";
}
var funcs=("\n" + abc.toString());

function def() {
for(var i=0; i<2; i++)
abc();
}
funcs += ("\n" + def.toString());
//I now have the funcs abc and def in string form that I'll put into my
dynamic script element

var s=document.createElement('SCRIPT');
if(navigator.userAgent.toLowerCase().indexOf("safari")!=-1)
s.innerHTML=funcs; //safari needs this way
else
s.text=funcs; //normal browsers need this

var b=document.getElementsByTagName('body')[0];
if(b)b.appendChild(s); //create those two funcs now

setTimeout(def,1000);
</script>

<div id='dd'></div>
</body></html>
 
D

dd

In addition to the local variable scope problem when deploying
functions like this, I also had problems with regexp replaces. Safari
doesn't like it and I'm not sure why:

var ss=s.replace(/ /g,"_"); //there's a space between the / /

To keep Safari happy I had to do this:

var ss=s;
while(ss.indexOf(" ")!=-1)ss=ss.replace(" ","_");

It might work in a regular inline function (I never bothered trying)
but if the code is deployed via a dynamic script block it's not happy.

I also have problems with nested ternary statements on Safari. This
example works on all browsers except Safari:

eval("foo"+a+"(a"+(arg>1?",pan":"")+(arg>2?",klr":"")+(arg>3?",iac":"")+(arg>4?",vwt":"")+(arg>5?",vau":"")+(arg>6?",cjs":"")+")");

It seems to be too complex for Safari's JS parser to handle.

I feel sorry for Safari users.
 
R

RobG

dd said:
Hi,

I've discovered a scenario where Safari 1.3 (I need to make my stuff
compliant with 1.3+) gets confused about the scope of local variables
WITHIN functions that were created in dynamic script blocks.

You will find that it is extremely difficult to find a general solution
for dynamically creating script elements:

<URL:
http://groups.google.com.au/group/c...ipt+element+ie7&rnum=1&hl=en#5aebcff147d2e442
I also think you are wrong about the apparent error - it does 'work' in
Safari, but the character string '<br>' is treated differently because
you add the script content with innerHTML.

I've made
this example where function def has a local i variable in a loop, and
it calls function abc which also has a local i variable in a loop. What
happens is that Safari is not respecting the scope and is allowing the
called function to corrupt a local variable in the parent function

You attempt to create two duplicate functions with the same name in the
same document, since they have (nearly) identical bodies, you won't
know which one is called. You also attempt to add an element to the
document body from within a declared head element - it is almost
certain that the body doesn't exist, so your extra script element is
never added. What your script calls is the functions defined in the
original script element, not the new ones you think you've added.

If you define your functions using strings and use your script adding
code onload, it "works" in the above browsers (code below). Of course
then you have issues with some characters such as "<" being interpreted
differently by .text and .innerHTML. I've changed the function to use
a text node and remove browser sniffing, but now it wont work in IE -
fun, isn't it? :)


<script type="text/javascript">

var funcs =
'function abc() { '+
' for(var i=0; i<1; i++) '+
' document.getElementById("dd").innerHTML+="in abc<br>"; '+
'} ';

funcs +=
'function def() { '+
' for(var i=0; i<2; i++) '+
' abc(); '+
'} ';

window.onload = function(){
var s = document.createElement('SCRIPT');
s.appendChild(document.createTextNode(funcs));
document.body.appendChild(s);
def();
}

</script>

<div id="dd"></div>


One solution is to use eval:

window.onload = function(){
eval(funcs);
def();
}

And be aware of the consequences of using eval in this context - the
scope in which the functions are created is changed, you need to take
other measures if you want variables declared inside the eval'd string
to be available after the onload function runs.
 
R

RobG

dd said:
In addition to the local variable scope problem when deploying
functions like this, I also had problems with regexp replaces. Safari
doesn't like it and I'm not sure why:

var ss=s.replace(/ /g,"_"); //there's a space between the / /

As normal code, Safari has not issues with that at all. If you are
referring to code inserted using innerHTML, then it is a manifestation
of incorrectly quoting characters when using innerHTML to insert the
script element content.

Since you are using the same string for both innerHTML[1] and .text,
the solution is to not do that and use one of the methods indicated in
my previous reply.


[...]
I also have problems with nested ternary statements on Safari. This
example works on all browsers except Safari:

eval("foo"+a+"(a"+(arg>1?",pan":"")+(arg>2?",klr":"")+(arg>3?",iac":"")+(arg>4?",vwt":"")+(arg>5?",vau":"")+(arg>6?",cjs":"")+")");

Are you again referring to code inserted using innerHTML? If so, see
previous comments/posts. Otherwise, post a small working example that
displays the error. I'm not going to guess all the likely values of
all those variables to test it otherwise.

It seems to be too complex for Safari's JS parser to handle.

I don't think it is. However, it does seem far too complex to maintain
and there is almost certainly a better way to achieve the same result
in maintainable code and without eval. It seems like you should
investigate the Function apply method.

I feel sorry for Safari users.

A misguided sentiment, I think your problems are of your own making.


1. innerHTML is a proprietary MS invention that has been widely copied
in other browsers. It has no public standard and implementations
differ. It should only be used as a convenience method to insert or
extract simple, fully-formed HTML elements.
 
D

dd

Thanks for this and the other reply in this thread RobG, they're very
informative. Some of the things you pointed out are purely based on my
trying to take my actual problem and bring it down to a simple
self-contained example. What I do actually have in my "real" page is
the funcs as a string (and not declared inline) and there's no <br>
involved either.

What I have learned from your example though is to give createTextNode
a try, and also perhaps give eval a try. I use eval in my Win/IE logic
but had to use dynodes for Gecko and thought I'd need to for Safari
too. I'll give Safari a shot at using eval too. I'll feed back what the
results were. Thanks a lot :)
 
D

dd

RobG said:
A misguided sentiment, I think your problems are of your own making.
Rob

You're right of course, frustration setting in, you know what it's
like.

That tip of createTextNode has worked, I just tried it. No doubt all
those problems (the regexp, the ternary prob and the everything else
seems to be solved by doing this. I gotta paypal you $50, easily
deserved :) I'll email you separately.

btw, I discovered the .apply(this,arguments) coincidentally a week ago
and used it elsewhere. I'll be applying it to that "bad" code in my
previous post when I get to it :)
 
R

Randy Webb

dd said the following on 1/6/2007 4:51 AM:
You're right of course, frustration setting in, you know what it's
like.

That tip of createTextNode has worked, I just tried it. No doubt all
those problems (the regexp, the ternary prob and the everything else
seems to be solved by doing this. I gotta paypal you $50, easily
deserved :) I'll email you separately.

I want my half Rob, I will sue you!!!! Wait, would that be in the US
Courts, Australian Courts, European Courts (ECMA is in Europe) or in the
court of my imagination?
<G>
 
R

RobG

Randy said:
dd said the following on 1/6/2007 4:51 AM:

I want my half Rob, I will sue you!!!! Wait, would that be in the US
Courts, Australian Courts, European Courts (ECMA is in Europe) or in the
court of my imagination?
<G>

I'll promise you 100% of what I received - and since I didn't take up
the offer, you can guess the rest... :)
 
R

Randy Webb

RobG said the following on 1/6/2007 8:03 PM:
I'll promise you 100% of what I received - and since I didn't take up
the offer, you can guess the rest... :)

What a nice guy you are :)
 

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

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,194
Latest member
KarriWhitt

Latest Threads

Top