Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the position of an iframe relative to the top window's viewport?

I've got html like so:

<body>
    [some stuff]
    <iframe src="pageWithMyScript.html"></iframe>
    [more stuff]
</body>

I want to find the location of the iframe relative to window.top (and/or the top.document) from a script running inside the iframe. (Ideally this would be without any frameworks, though I can always deconstruct how they do it, I suppose.)

like image 599
sprugman Avatar asked Nov 02 '25 10:11

sprugman


2 Answers

This can only work if both the iframe and the container share the same origin, otherwise CORS will have to be set up (to do this you will need access to both domains)

/**
 * Calculate the offset of the given iframe relative to the top window.
 * - Walks up the iframe chain, checking the offset of each one till it reaches top
 * - Only works with friendly iframes. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Cross-origin_script_API_access 
 * - Takes into account scrolling, but comes up with a result relative to 
 *   top iframe, regardless of being visibile withing intervening frames.
 * 
 * @param window win    the iframe we're interested in (e.g. window)
 * @param object dims   an object containing the offset so far:
 *                          { left: [x], top: [y] }
 *                          (optional - initializes with 0,0 if undefined) 
 * @return dims object above
 */
var computeFrameOffset = function(win, dims) {
    // initialize our result variable
    if (typeof dims === 'undefined') {
        var dims = { top: 0, left: 0 };
    }

    // find our <iframe> tag within our parent window
    var frames = win.parent.document.getElementsByTagName('iframe');
    var frame;
    var found = false;

    for (var i=0, len=frames.length; i<len; i++) {
        frame = frames[i];
        if (frame.contentWindow == win) {
            found = true;
            break;
        }
    }

    // add the offset & recur up the frame chain
    if (found) {
        var rect = frame.getBoundingClientRect();
        dims.left += rect.left;
        dims.top += rect.top;
        if (win !== top) {
            computeFrameOffset(win.parent, dims);
        }
    }
    return dims;
};
like image 172
sprugman Avatar answered Nov 04 '25 00:11

sprugman


Most answers do not consider border and padding of the frame element.
Sometimes they are not 0, so it should be included in the conversion process.
Actual border and padding size of an element can be acquired from the "computed style".

    function getTopBoundingClientRect( e )
    {
        const rcInit = e.getBoundingClientRect();
        let f = e.ownerDocument.defaultView.frameElement;
        if ( !f )
            return rcInit;
        const rc = { left: rcInit.left, top: rcInit.top, right: rcInit.right, bottom: rcInit.bottom };
        do {
            const frc = f.getBoundingClientRect();
            const cs = f.ownerDocument.defaultView.getComputedStyle( f );
            const lm = +cs.getPropertyValue( 'border-left-width' ).slice( 0, -2 )
                + +cs.getPropertyValue( 'padding-left' ).slice( 0, -2 );
            const tm = +cs.getPropertyValue( 'border-top-width' ).slice( 0, -2 )
                + +cs.getPropertyValue( 'padding-top' ).slice( 0, -2 );
            rc.left += frc.left + lm;
            rc.top += frc.top + tm;
            rc.right += frc.left + lm;
            rc.bottom += frc.top + tm;
            f = f.ownerDocument.defaultView.frameElement;
        } while ( f );
        return rc;
    }

Of course, in cross-domain iframe, any code cannot access parent document (especially frameElement in above code.)

like image 40
MoonSung Zhang Avatar answered Nov 03 '25 23:11

MoonSung Zhang



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!