Событие Javascript неправильно устанавливается при закрытии, когда вызывается кросс-кадр
У меня есть следующий код в верхнем фрейме двухкадровой страницы:
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;
Это работает при загрузке исходного документа, но когда я вызываю эту функцию из другого кадра (обычно, когда перезагружается только один кадр), ловушка устанавливается, но когда срабатывает функция onkeydown, она не получает соответствующие аргументы, вместо этого evt == ноль.
Полный код следует:
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>
Чтобы воссоздать проблему, щелкните правой кнопкой мыши нижнюю рамку, нажмите "Обновить", нажмите "Сбросить зацепки" и нажмите клавиши.
Связанный вопрос: обрабатывать keyPress через кадры в IE
Я также прочитал статью о Javascript замыканиях, но я не уверен, как это применимо.
Извините за узость вопроса, но я действительно не знаю Javascript достаточно хорошо, чтобы разобраться в этом.
2 ответа
Вот решение, которое отбрасывает "старый способ" обработки событий и вместо этого использует более гибкую и мощную модель "прослушивателя событий". Это позволяет передавать объекты событий
Обратите внимание, что метод attachEvent() предназначен только для IE (который вы указали как нормальный в предыдущем посте - но вам придется изменить это, если вы поддерживаете что-то еще)
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 );
}
Когда прослушиватели событий регистрируются таким образом, соответствующий объект события автоматически передается в качестве аргумента функции-обработчику.
Когда делается ссылка на окно, оно разрешает объект окна вызывающей области, а не резидентную область.
Это означает, что когда вы повторно присоединяете прослушиватели событий, выполняя setKeyHook() из нижнего фрейма, ссылка на окно такая же, как top.frames.BOTTOM, который не является окном, которое вы хотите использовать - вы хотите событие из окна TOP - которое имитирует первоначальное использование этой функции.
Таким образом, вы сможете исправить это, используя явную ссылку на событие в TOP-кадре.
function setKeyHook()
{
logMessage("setKeyHook()");
top.frames.BOTTOM.document.onkeydown =
top.frames.TOP.document.onkeydown = function( evt )
{
return function(){
top.frames.TOP.handleKeypress(evt);
};
}( top.frames.TOP.event ); // here
}