IE events expire (typeof = unknown) after callStack finishes. (featuring: setTimeout scoping)

P

phocis

I wrote a pair of functions to enable scoped or referenced setTimeout
calls. I did this because I have an object factory that creates
multiple objects and those objects need to delay a few calls on
themselves at a certain point in time.

This code works fine for normal objects. However, my issue occurs if
at any point, an event object is passed across in IE.

It seems, that events methods and variables in IE are PRIVATE (!)
after the initial callstack has been finished.

The following is the code required to view this bug(?) in IE, and
success in FF.

<html>
<head>
<script>
/*****************************************
Here is the code for the delaying:
******************************************/
// Global variables.
var delayObjects = {}; // Hold the object references.
var delayObjectsIndex = 0; // Keep track of the index.

function delayObjectMethodCall(obj,exp,ms){
delayObjectsIndex++;
delayObjects[delayObjectsIndex] = obj; // This assigns
by reference.

// This is the eval that is to be run.
expNew = '\
if(delayObjects['+delayObjectsIndex+']){\
with(delayObjects['+delayObjectsIndex+']){\
'+exp+';\
}\
delayObjects['+delayObjectsIndex+'] =
undefined}\
';
document.getElementById('result').innerHTML = 'Delay
was called: (event.keyCode = '+obj.event.keyCode+')<br/
'+document.getElementById('result').innerHTML;
setTimeout(expNew, ms);
return delayObjectsIndex;
}
function delayObjectMethodCallCancel(id){
if(typeof id != 'undefined' && delayObjects[id]){
clearTimeout(id);
delayObjects[id] = undefined;
return true;
}
}


/*********************************************
* Onload

********************************************/
window.onload = function(){
document.getElementById('test').onkeydown=function(e){
if(!e){e=window.event;}
document.getElementById('result').innerHTML =
'Event was fired: (event.keyCode = '+e.keyCode+')<br/
'+document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML =
'Before Delay: Input Value: '+this.value + '<br/
'+document.getElementById('result').innerHTML;
// This is the call
delayObjectMethodCall(
// Object To pass
{input:this,event:e,other:'staticObject'},
// String to eval
"\

document.getElementById('result').innerHTML = 'START OF DELAY
PAYLOAD<br/>'+document.getElementById('result').innerHTML;\

document.getElementById('result').innerHTML = 'After Delay: Input
Value: '+input.value + '<br/
'+document.getElementById('result').innerHTML;\

document.getElementById('result').innerHTML = 'After Delay: Other
Value: '+other + '<br/>'+document.getElementById('result').innerHTML;\
try{\

document.getElementById('result').innerHTML = 'Event keyCode:
'+event.keyCode + '<br/>'+document.getElementById('result').innerHTML;
\
}catch(e){\

document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error: said:
'+document.getElementById('result').innerHTML;\

document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error: said:
'+document.getElementById('result').innerHTML;\

document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error: said:
'+document.getElementById('result').innerHTML;\
}\

document.getElementById('result').innerHTML = 'END OF DELAY PAYLOAD<br/
<br/>'+document.getElementById('result').innerHTML;\
",
100
);
return true;
}
}
</script>
</head>
<body>
<input id="test" type="text" value="change this somehow"/>
<input type="button" value="clear"
onclick="document.getElementById('result').innerHTML = '';"/>
<div id="result"></div>
</body>
</html>
 
P

phocis

I wrote a pair of functions to enable scoped or referenced setTimeout
calls. I did this because I have an object factory that creates
multiple objects and those objects need to delay a few calls on
themselves at a certain point in time.
This code works fine for normal objects. However, my issue occurs if
at any point, an event object is passed across inIE.
It seems, thateventsmethods and variables inIEare PRIVATE (!)
after the initial callstack has been finished.
The following is the code required to view this bug(?) inIE, and
success in FF.
<html>
<head>
<script>
/*****************************************
Here is the code for the delaying:
******************************************/
// Global variables.
var delayObjects = {}; // Hold the object references.
var delayObjectsIndex = 0; // Keep track of the index.
function delayObjectMethodCall(obj,exp,ms){
delayObjectsIndex++;
delayObjects[delayObjectsIndex] = obj; // This assigns
by reference.
// This is the eval that is to be run.
expNew = '\
if(delayObjects['+delayObjectsIndex+']){\
with(delayObjects['+delayObjectsIndex+']){\
'+exp+';\
}\
delayObjects['+delayObjectsIndex+'] =
undefined}\
';
document.getElementById('result').innerHTML = 'Delay
was called: (event.keyCode = '+obj.event.keyCode+')<br/>'+document.getElementById('result').innerHTML;
setTimeout(expNew, ms);
return delayObjectsIndex;
}
function delayObjectMethodCallCancel(id){
if(typeof id != 'undefined' && delayObjects[id]){
clearTimeout(id);
delayObjects[id] = undefined;
return true;
}
}
/*********************************************
* Onload
********************************************/
window.onload = function(){
document.getElementById('test').onkeydown=function(e){
if(!e){e=window.event;}
document.getElementById('result').innerHTML =
'Event was fired: (event.keyCode = '+e.keyCode+')<br/>'+document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML =
'Before Delay: Input Value: '+this.value + '<br/>'+document.getElementById('result').innerHTML;
// This is the call
delayObjectMethodCall(
// Object To pass
{input:this,event:e,other:'staticObject'},
// String to eval
"\
document.getElementById('result').innerHTML = 'START OF DELAY
PAYLOAD<br/>'+document.getElementById('result').innerHTML;\
document.getElementById('result').innerHTML = 'After Delay: Input
Value: '+input.value + '<br/

document.getElementById('result').innerHTML = 'After Delay: Other
Value: '+other + '<br/>'+document.getElementById('result').innerHTML;\
try{\
document.getElementById('result').innerHTML = 'Event keyCode:
'+event.keyCode + '<br/>'+document.getElementById('result').innerHTML;
\
}catch(e){\
document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error:</span> '+e.message + '<br/

document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error:</span> typeof event: '+typeof event+'<br/

document.getElementById('result').innerHTML = '<span style=\"color:red
\">Error:</span> typeof event.keyCode: '+typeof event.keyCode+'<br/>'+document.getElementById('result').innerHTML;\

document.getElementById('result').innerHTML = 'END OF DELAY PAYLOAD<br/><br/>'+document.getElementById('result').innerHTML;\
",
100
);
return true;
}
}
</script>
</head>
<body>
<input id="test" type="text" value="change this somehow"/>
<input type="button" value="clear"
onclick="document.getElementById('result').innerHTML = '';"/>
<div id="result"></div>
</body>
</html>

When you look at your code and you see a lot of repetition, even
visually, that's a clue that you should re-examine your style (Don't
Repeat Yourself - Especially When Asking Other People To Look At Your
Code).

1. Try to come up with a way to do this without using "eval". (Hint:
take a look at function closures.) See:http://jibbering.com/faq/#FAQ4_40http://javascript.crockford.com/code.html(end).
2. Since the main element you're working with is 'result', why not
just assign it to a variable and avoid the repeated
"document.getElementById" for readability. Ditto with the ubiquitous
multi-line strings (which will go away after you take care of #1).

As for your "event" object problem, the "event" object's properties
are not "private", a concept which does not exist in JavaScript, at
least not in any way analogous to C++ or Java. The problem here, as
your example demonstrates, is as follows: after the called event
handler exits and its scope chain is broken down, the window.event
object is cleared for the next event and any references to it are no
longer valid. You should instead pass in any specific values (such as
keyCode) to your literal, as these will be preserved across the
boundary.

-David

In response to your first point:
Closures will not work for what I'm trying to accomplish. The only way
to process a delayed execution is with either eval or setTimeout. I
cannot imagine that there is any other obvious way to do it. And what
I was trying to share was that it is possible to pass object
references with it using my two functions.

In response to your second point:
All of the repetition is for the display of the testing logic.
Granted, I guess it's still bad to have, but I wanted things to be as
straight forward as possible. As that was my first post to a
newsgroup, I was unaware that my message would be formatted to a set
column size. I apologize for the resulting obfuscation.
 

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,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top