开发者

fixed vertical positioning of css within an iframe

I am trying to get my bottom header to stick to the bottom of the screen inside of my iframe application and have it always appear in view for the user even when the page is scrolling. I have no control over the outer iframe as it is on a different domain. The header itself must be inside of开发者_如何学C the iframe as I have no control outside the iframe. The iframe always expands to the height of its contents so that it has no scrollbars, but the bar still has to be visible in the viewport at all times.

Another thing to note: The iframe height should be the same height as its contents so their is no need for scroll bars


Chrome has a bug that doesn't fix elements with position:fixed if:

a) you use CSS3 transform in any element, and/or

b) you have a child element positioned outside the box of it's parent element

Oddly enough, the bug was reported back in 2009 and it's still open: https://code.google.com/p/chromium/issues/detail?id=20574


You might want to play around with position: fixed;

#element {
    position: fixed;
    z-index: 1000;
    bottom: 0;
}

EDIT:

I'm sorry, I think I miss understood your post. If I'm reading it correctly you want to create a header bar similar to blogger but to keep it always in view of the user when he/she scrolls.

What you can do is create a container div, and then you can nest both your header and iframe inside that container. You can then play around with the positioning, although I'm not sure if the exact behavior that you're looking for is possible without some javascript.

EDIT 2:

After playing around a bit, I got something that I think might help (if I understand your problem correctly).

http://digitaldreamer.net/media/examples/iframe-site.html http://digitaldreamer.net/media/examples/iframe.html


I had to look for a long time for a possible solution, and I think I have found one that is using the Intersection Observer API to detect the scrolled position of the iframe within the parent document without needing to access the parent document DOM.

I'm creating a bunch of hidden 100px high elements in the iframe. These are positioned absolutely underneath each other so that together they fill the height of the whole iframe document. An intersection observer then observes the intersection between the (top-level document) viewport and each of the hidden elements and calculates the scroll position of the iframe based on the values it returns. A ResizeObserver creates additional hidden elements if the height of the body increases.

This approach assumes that your iframe is always minimum 100px high. If you expect a smaller height, you need to adjust the hidden container height. The reason is that once a hidden container is 100% visible, the intersection observer does not emit the callback while the parent document is being scrolled (since the intersection ratio stays at 1). This is also the reason why I need a lot of small containers rather than observing the intersection with the iframe body itself.

const CONTAINER_HEIGHT = 100;
const threshold = [...Array(CONTAINER_HEIGHT + 1).keys()].map((i) => i / CONTAINER_HEIGHT);

/**
 * Registers an intersection handler that detects the scrolled position of the current
 * iframe within the browser viewport and calls a handler when it is first invoked and
 * whenever the scrolled position changes. This allows to position elements within the
 * iframe in a way that their position stays sticky in relation to the browser window.
 * @param handler Is invoked when the function is first called and whenever the scroll
 * position changes (for example due to the user scrolling the parent document). The
 * "top" parameter is the number of pixels from the top of the browser viewport to the
 * top of the iframe (if the top of the iframe is above the top of the browser viewport)
 * or 0 (if the top of the iframe is below the top of the browser viewport). Positioning
 * an element absolutely at this top position inside the iframe will simulate a sticky
 * positioning at the top edge of the browser viewport.
 * @returns Returns a callback that unregisters the handler.
 */
function registerScrollPositionHandler(handler: (top: number) => void): () => void {
    const elementContainer = document.createElement('div');
    Object.assign(elementContainer.style, {
        position: 'absolute',
        top: '0',
        bottom: '0',
        width: '1px',
        pointerEvents: 'none',
        overflow: 'hidden'
    });
    document.body.appendChild(elementContainer);

    const elements: HTMLDivElement[] = [];
    let intersectionObserver: IntersectionObserver | undefined = undefined;

    const resizeObserver = new ResizeObserver(() => {
        intersectionObserver = new IntersectionObserver((entries) => {
            for (const entry of entries) {
                if (entry.intersectionRatio > 0 && (entry.intersectionRect.top > entry.boundingClientRect.top || entry.target === elements[0])) {
                    handler(entry.intersectionRect.top);
                }
            }
        }, { threshold });

        const count = Math.ceil(document.documentElement.offsetHeight / CONTAINER_HEIGHT);
        for (let i = 0; i < count; i++) {
            if (!elements[i]) {
                elements[i] = document.createElement('div');
                Object.assign(elements[i].style, {
                    position: 'absolute',
                    top: `${i * CONTAINER_HEIGHT}px`,
                    height: `${CONTAINER_HEIGHT}px`,
                    width: '100%'
                });
                elementContainer.appendChild(elements[i]);
                intersectionObserver.observe(elements[i]);
            }
        }
    });
    resizeObserver.observe(document.documentElement);

    return () => {
        resizeObserver.disconnect();
        intersectionObserver?.disconnect();
        elementContainer.remove();
    };
}

This example code should create a toolbar that is sticky at the top of the browser viewport:

<div style="position: absolute; top: 0; left: 0; bottom: 0; right: 0; overflow: hidden; pointer-events: none; z-index: 90">
    <div id="toolbar" style="position: absolute; top: 0; left: 0; right: 0; pointer-events: auto; transition: top 0.3s">
        Line 1<br/>Line 2<br/>Line 3<br/>Line 4<br/>Line 5<br/>Line 6<br/>Line 7<br/>Line 8<br/>Line 9<br/>Line 10
    </div>
</div>
<script>
    registerScrollPositionHandler((top) => {
        document.querySelector('#toolbar').style.top = `${top}px`;
    });
</script>

Note that other than what you asked for, this will position the toolbar at the top of the viewport rather than at the bottom. Positioning at the bottom should also be possible, but is slightly more complex. If anyone requires a solution for this, please let me know in the comments and I will invest the time to adjust my answer.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜