I have the following code in the top frame of a two frame page:
function setKeyHook()
{
logMessage("setKeyHook()");
top.frames.BOTTOM.document.onkeydown =
top.frames.TOP.document.onkeydown = function( evt )
{
return function(){
top.frames.TOP.handleKeypress(evt);
};
}( window.event );
}
onload = setKeyHook;
This works on the original document load, but when I call this function from another frame (usually when only one frame reloads), the hook is set, but when the onkeydown
function fires, it does not receive the appropriate arguments, instead evt == null
.
Full Code follows:
KeyFrameTest.asp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<title>KeyFrameTest</title>
</head>
<frameset Rows="80%,20%">
<frame id="TOP" name="TOP" src="KeyFrameTestTop.asp">
<frame id="BOTTOM" name="BOTTOM" src="KeyFrameTestBottom.asp">
</frameset>
</html>
KeyFrameTestTop.asp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script type="Text/Javascript">
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g,"");
}
NumElements = 5;
//Alt Vals
ControlCode = 1;
ShiftCode = 2;
ControlShiftCode = 3;
//Key Vals
keyR = 82;
keyJ = 74;
keyI = 73;
keyT = 84;
keyEnter = 13;
//Array Indexs
AltIndex = 0;
KeyIndex = 1;
FuncIndex = 2;
KeyFuncMap = new Array(NumElements);
for (i = 0; i < KeyFuncMap.length; ++i)
{
//Three elements, control or shift, key, function
KeyFuncMap[i] = new Array(3);
}
KeyFuncMap[0][AltIndex] = ControlCode;
KeyFuncMap[0][KeyIndex] = keyR;
KeyFuncMap[0][FuncIndex] = "parent.TOP.logMessage(\"Ctrl + R\")";
KeyFuncMap[1][AltIndex] = ControlCode;
KeyFuncMap[1][KeyIndex] = keyJ;
KeyFuncMap[1][FuncIndex] = "parent.TOP.logMessage(\"Ctrl + J\")";
KeyFuncMap[2][AltIndex] = ControlCode;
KeyFuncMap[2][KeyIndex] = keyI;
KeyFuncMap[2][FuncIndex] = "parent.TOP.logMessage(\"Ctrl + I\")";
KeyFuncMap[3][AltIndex] = ControlCode;
KeyFuncMap[3][KeyIndex] = keyT;
KeyFuncMap[3][FuncIndex] = "parent.TOP.logMessage(\"Ctrl + T\")";
KeyFuncMap[4][AltIndex] = ControlCode;
KeyFuncMap[4][KeyIndex] = keyEnter;
KeyFuncMap[4][FuncIndex] = "parent.TOP.logMessage(\"Ctrl + Enter\")";
function CompleteEvent(e)
{
e.cancelBubble = true;
e.returnValue = false;
}
function logMessage(msg)
{
logBox = parent.TOP.document.getElementById("logBox");
if( logBox.value.trim().length < 1 )
{
logBox.value = msg;
}
else
{
logBox.value = logBox.value + "\r\n" + msg;
}
}
function handleKeypress(e)
{
logMessage("handleKeypress(e)");
e = e || window.event ;
if (e == null)
{
logMessage("handleKeypress(e): e == null");
return false;
}
controlVal = getControlVal(e);
for (i = 0; i < KeyFuncMap.length; i++)
{
if (KeyFuncMap[i][AltIndex] == controlVal &&
KeyFuncMap[i][KeyIndex] == e.keyCode)
{
eval(KeyFuncMap[i][FuncIndex]);
CompleteEvent(e);
}
}
}
function getControlVal(e)
{
if (e.ctrlKey && e.shiftKey)
{
return 3;
}
else if (e.ctrlKey)
{
return 1;
}
else if (e.shiftKey)
{
return 2;
}
else return 0;
}
function displayEverything()
{
displayProps(top.frames.TOP, "top.frames.TOP", 0, 1);
displayProps(top.frames.BOTTOM, "top.frames.BOTTOM", 0, 1);
}
function clearLog()
{
logBox = parent.TOP.document.getElementById("logBox");
logBox.value = "";
}
function displayProps(o, name, level, maxLevel)
{
try {
if (level > maxLevel)
return;
for (prop in o){
logMessage(name + "." + prop + " = " + o[prop]);
if (typeof(o[prop]) == "object" && o[prop] != o){
displayProps(o[prop], name + "." + prop, level + 1, maxLevel);
}
}
}
catch (ex){
logMessage(ex.toString());
}
}
function setKeyHook()
{
logMessage("setKeyHook()");
top.frames.BOTTOM.document.onkeydown =
top.frames.TOP.document.onkeydown = function( evt )
{
return function(){
top.frames.TOP.handleKeypress(evt);
};
}( window.event );
}
onload = setKeyHook;
</script>
</head>
<body>
<h1>Hello</h1>
<textarea id="LogBox" rows="20" cols="80"></textarea><BR>
<input type="Button" value="Display Properties" onClick="displayEverything();"/>
<input type="Button" value="Clear Log" onClick="clearLog();"/>
</body>
</html>
KeyFrameTestBottom.asp
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
</head>
<body>
<p>Press Keys Here</p>
<input type="Button" value="Reset Handlers"
onclick="top.frames.TOP.setKeyHook();">
</body>
</html>
To recreate the issue, right click the bottom frame, click refresh, click "Reset Hooks", and press keys.
Related Question: Handle keyPress Accross Frames in IE
I've also read an article on Javascript closures, but I'm not sure how it applies.
Sorry for the narrowness of question, but I really don't know Javascript well enough to figure out the trick to this.
Here's a solution that ditches the "old way" of handling events, and instead uses the more flexible and powerful "event listener" model. This allows the passing of event objects
Note that the attachEvent() method is IE only (which you stipulated as being ok in your previous post - but you'll need to change this if you support something else)
function setKeyHook()
{
var botDocument = top.frames.BOTTOM.document;
var topDocument = top.frames.TOP.document;
var eventName = 'onkeydown';
var handlerFunc = top.frames.TOP.handleKeypress;
// Clear them first, or else they'll attach twice and thusly, fire twice
botDocument.detachEvent( eventName, handlerFunc );
topDocument.detachEvent( eventName, handlerFunc );
topDocument.attachEvent( eventName, handlerFunc );
botDocument.attachEvent( eventName, handlerFunc );
}
When event listeners are registered this way, the proper event object is automatically passed as an argument to the handler function.