views:

525

answers:

2

I've been trying to handle the onkeydown event across multiple frames (no, I unfortunately cannot be rid of the frames) via javascript (see my previous question here). I'm getting a handle on the document in the other frame, and setting it's onkeydown handler equal to my function. No error is thrown, but when I later check the document's settings, onkeydown is null. I've gotten the same results in IE6 and IE7. What am I doing wrong.

  • Function

        function setKeyHook(doc)
        {
            try{               
                if (doc)
                    if (parent.TOP.handleKeypress){
                        doc.onkeydown = parent.TOP.handleKeypress;
                        logMessage('Attached handler');
                    }
                    else{
                        logMessage('No handleKeypress');
                    }
                else
                    logMessage('No doc');
            }
            catch (ex){
                logMessage(ex.toString());
            }
        }
    
  • Call

    setTimeout("setKeyHook(parent.document.getElementById(\"bottom\").document);", 1000);

  • Output

   Attached handler
  • After execution
    BOTTOM.protocol = HyperText Transfer Protocol
    BOTTOM.onkeypress = null
    BOTTOM.onrowenter = null
    BOTTOM.onmousedown = null

How should I apply the same event handler across frames?

Note: This needs to work (and only work) in IE6 and IE7.

+2  A: 

I got this to work

frameset.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd"&gt;
<html>
<head>
    <title>Test</title>
</head>

frame1.html

<frameset rows="50%,50%">
    <frame src="frame1.html" name="TOP">
    <frame src="frame2.html" name="BOTTOM">
</frameset>

</html>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd"&gt;
<html>
<head>
    <title>Frame 1</title>
    <script type="text/javascript">
    onload = function()
    {
     top.frames.BOTTOM.document.onkeydown = 
     self.document.onkeydown = function( evt )
     {
      return function()
      {
       // Just an example to show it's working
       document.getElementById( 'output' ).value += String.fromCharCode( evt.keyCode );
      }
     }( window.event );
    }

    </script>
</head>
<body>
    frame1
    <textarea id="output"></textarea>  
</body>
</html>

frame2.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
   "http://www.w3.org/TR/html4/frameset.dtd"&gt;
<html>
<head>
    <title>Frame 2</title>
</head>
<body>
    frame2
    <textarea></textarea>
</body>
</html>

First of all, it always pays to be fairly explicit with your object references. Using proprietary DOM shortcuts (such as window.frameName) just add unnecessary error potential. This is why my script explicitly looks in the frames collection of the window object.

Next is to familiarize yourself with the various built-in window references in the DOM when dealing with framesets. There are 4 total

  1. window - The current window. This is also implied when left out (i.e., window.onload === onload)
  2. parent - The parent window to the current
  3. top - The topmost window in a frameset family. parent === top when you have only one frameset.
  4. self - alias of window

So basically what I've done here is, when the onload event fires in the frame1 window, is add a handler function to the keydown event for the documents in both of the frame windows.

That function uses a closure to ensure that the event generated in frame1 is available to the actual handler, regardless of which window generated the keydown event. This is necessary because of how IE does events. Whereas other browser create new event objects as they occur and passes them to event hanlders, IE just modifies the global event object (referenced via window.event or more simple, just event) to reflect the current event.

I hope that makes sense.

Peter Bailey
Thank you, can you explain why this works?
C. Ross
Edited! I did my best to explain it without becoming too long winded
Peter Bailey
When I call this function from another frame, the closure doesn't work (the hook gets set, but only ever gets passed null for evt). Why is this, and how can I correct it?
C. Ross
When you say "call this function from another frame" you're going to need to be more explicit. Because as written above, it works. Are you trying to do something else now?
Peter Bailey
A: 

If you're having a problem reading the event object of a frame when the event is handled in another frame, you'll have to reference it explicitly

var event = top.frames.BOTTOM.event;

I've run into this issue myself, and the beginning of my event handler was

var myFrame = top.frames[0];
myFrame.onkeydown = keyboardHandler;

function keyboardHandler(e) {
    if (!e && event) {
        e = event; //For handling the event in IE
    }
    if (!e && myFrame.contentWindow.event) {
        e = myFrame.contentWindow.event; //For handling event in IE6 from inside the iFrame
    }

    if (e) {
    ...
    }
}