To add event listeners to all the frames in a document

Viewed 1137

I am working on something which requires me to add mouse/keyboard event listeners to the document on document load.

document/window.addEventListener() worked well until I came across Frameset/frames/iframes.

I did some workaround, like iterating through all the frames of frameset and added event-listeners to them.

Then I realized frames are loaded dynamically, after DOM. So I've done something like this:

    bindListenersToFrame: function(element){
        var $ = domUtils.jQuery;
        $(element).ready(function(){
            if(element.tagName == "FRAMESET" || element.tagName == "BODY"){
                    for(var i=0; i < element.children.length; i++){
                            domUtils.bindListenersToFrame(element.children[i]);
                    }
            }
            if(element.tagName == "FRAME" || element.tagName == "IFRAME"){
                $('iframe').load(function(){
                    domUtils.addListener(element);
                    if(element.contentDocument.documentElement){
                            for(var i=0; i < element.contentDocument.documentElement.children.length; i++){
                                    domUtils.bindListenersToFrame(element.contentDocument.documentElement.children[i]);
                            }
                    }
                });
            }
        });
}

Above method is recursive in nature and domUtils is just a object with methods like "addListener".

Any help would be greatly appreciated. Thank you.

3 Answers

Try this:

$('body').on('click','iframe',function(e) {
       console.log("clicked");
});

I would suggest recursively checking for frames and attaching listeners. This really shouldn't be done, but it's the only way. Be aware this snippet does not work on SO due to sandboxing restrictions on snippets. I have tested it and it works locally an a server. I am also not able to find (in the limited time I have) a way around the setTimeout I am currently using. It feels like it gets applied before the page is loaded, but I can't see any effect from these load events, so for the sake of just showing how it could work, please check this:

function nestedIFrameEventAttacher( iframe, event, handler ){

  const content = iframe.contentDocument || (iframe.contentWindow ? iframe.contentWindow .document : iframe);
  const body = content.querySelector( 'body' );
    
  body.addEventListener( event, handler );
  
  // I have tried attaching `load` and `DOMContentLoaded` events
  // to the `iframe` and the `body` but none of them seemed to trigger. 
  // This is therefor not guaranteed to work, but you can start from this.
  setTimeout(function(){
  
    Array.from( body.querySelectorAll( 'iframe' ) ).forEach(iframe => {

      nestedIFrameEventAttacher( iframe, event, handler );

    });
  
  }, 100);

}

const output = document.getElementById( 'output' )

nestedIFrameEventAttacher( document, 'click', function( event ){
    
    output.textContent = this.querySelector( 'h1' ).textContent;

});
#output{ color: red; }
<div id="output">Stealie the Dog</div>
<h1>Root</h1>
<iframe srcdoc="<html><head></head><body><h1>Second</h1><iframe srcdoc='<html><head></head><body><h1>Deeper</h2></body></html>'></iframe></body></html>" width="100%" height="500px"></iframe>

Why not add the listener to <body>?

$('body').click(function(e) {
    console.log("clicked")
})
Related