postMessage Source IFrame

前端 未结 6 1893
执念已碎
执念已碎 2020-12-13 15:21

I\'m working on a website with cross-domain iframes that are resized to the correct height using postMessage. The only problem I\'m having is identifying which iframe has wh

相关标签:
6条回答
  • 2020-12-13 15:42

    If the source iframe is nested in more than one parent iframe, then you will need to recurse over the window.frames property of each iframe and compare it against the messageEvent#source property.

    For example, if the message is generated by iframe#level3 of this Dom.

    <iframe Id=level1>
       <iframe Id=level2>
           <iframe Id=level3 />
       </iframe>
    </iframe>
    

    You should be able to find the index of the ancestor iframe in the current window using.

    FindMe = event.source
    FrameIndex = find(window)
    frames[FrameIndex].frameElement ==     getElByTagName(iframe)[FrameIndex] 
    
    function find(target){
        for (i=0; I< target.frames.length; i ++)
           if(target.frames[i] == FindMe ||   find(target.frames[i]))
               return i
        return false 
    }
    

    Its important to note that

    The Window.frames property is not restricted by cross domain policy

    This technique will work regardless of how deeply nested the source iframe happens to be

    window.frames is a collection of window objects not iframe elements.

    access to the propetties s of the memebers of the window.frames collection is resttictred by Same Origin (I.e you may not be able to access the frameElement or location properties of window.frames[i]

    0 讨论(0)
  • 2020-12-13 15:44

    The event should also have a property "source" that can be compared to the iframes "contentWindow" property.

    0 讨论(0)
  • 2020-12-13 15:53

    I have found the solution from here: How to share a data between a window and a frame in JavaScript

    Parent:

    var frames = document.getElementsByTagName('iframe');
    for (var i = 0; i < frames.length; i++) {
        if (frames[i].contentWindow === event.source) {
            $(frames[i]).height(event.data); //the height sent from iframe
            break;
        }
    }
    
    0 讨论(0)
  • 2020-12-13 15:57

    The following works for me cross-origin:

    window.addEventListener('message', function (event) {
      if (event.data.size) {
        Array.prototype.forEach.call(document.getElementsByTagName('iframe'), function (element) {
          if (element.contentWindow === event.source) {
            element.style.height = `${event.data.size.height}px`;
          }
        });
      }
    }, false);
    

    Tested in Chromium 64 and Firefox 59.

    0 讨论(0)
  • 2020-12-13 16:01

    i have an idea to solve this issue. when you create the iframe give a name/id to the iframe. .

    and, in the script inside iframe send the message as object which will look like

    window.parent.postMessage({"height" : $('.widget').outerHeight(), "frmname" : window.name}, '*');
    

    in the parent listener,

    eventer(messageEvent, function(e) {`enter code here`
        $(e.data.frmname).height(e.data.height);
    }, false);
    
    0 讨论(0)
  • 2020-12-13 16:04

    Yes, you can identify the IFRAME which did the postMessage. There are different situations:

    • the source IFRAME has the same-origin URL (e.g. http://example.com/) as the Window which receives the message: the IFRAME is identified using

      myIFRAME.contentWindow == event.source

    • the source IFRAME has a same-origin but relative URL (e.g. /myApp/myPage.html) to the parent HTML page: the IFRAME is identified using

      myIFRAME.contentWindow == event.source.parent

    • the source IFRAME has a cross-origin URL (e.g. http://example.com/) different of the page which receives the message (e.g http://example.org/): the above methods do not work (the comparison is always false and accessing properties of event.source lead to Access Deniederrors) and the IFRAME must be identified based on its origin domain;

      myIFRAME.src.indexOf(event.origin)==0

    In order to manage these three different situations, I'm using the following:

    var sourceFrame = null; // this is the IFRAME which send the postMessage
    var myFrames = document.getElementsByTagName("IFRAME");
    var eventSource = event.source; // event is the event raised by the postMessage
    var eventOrigin = event.origin; // origin domain, e.g. http://example.com
    
    // detect the source for IFRAMEs with same-origin URL
    for (var i=0; i<myFrames.length; i++) {
        var f = myFrames[i];
        if (f.contentWindow==eventSource || // for absolute URLs
            f.contentWindow==eventSource.parent) { // for relative URLs
            sourceFrame = f;
            break;
        }
    }
    
    // detect the source for IFRAMEs with cross-origin URL (because accessing/comparing event.source properties is not allowed for cross-origin URL)
    if (sourceFrame==null) {
        for (var i=0; i<myFrames.length; i++) {
            if (myFrames[i].src.indexOf(eventOrigin)==0) {
                sourceFrame = myFrames[i];
                break;
            }
        }
    }
    

    For cross-domain URLs, note that we cannot differentiate the true source if event.origin is a domain common to more than one IFRAMEs.

    Some people use === instead of == but I did not found any difference in this context, so I'm using the shortest comparator.

    This implementation has been tested and works under:

    • MSIE 9
    • Firefox 17

    As an alternative (suggested by Griffin), you could use a IFRAME src with an unique idenfitier (e.g. timestamp), and the IFRAME'd web application will send back the this unique identifier in the posted messages. While the IFRAME identification would be simpler, this approach requires to modify the IFRAME'd web application (which is not always possible). This also may lead to security issues (e.g. the IFRAME'd web application tries to guess the unique identifier of other IFRAMEs applications).

    0 讨论(0)
提交回复
热议问题