CSS vh units inside an iframe

2019-02-05 21:05发布

问题:

I'm trying to use CSS vh units inside of an iframe. I'm finding that they are scaled to the size of the iframe somehow. In other words, 100vh isn't the windowheight. It's set to the height of the iframe.

Does this seem right?

Is there a workaround?

回答1:

I know this is an old question, but as people move toward the vh unit, this question will become much more common.

To clarify, here's an example of the problem. We have an HTML file that loads an iframe:

<!DOCTYPE html>
<html>
<head></head>
<style>
iframe {
    height: 50vh;
    width: 100%;
}
</style>
<body>
    <iframe src="iframe.html"/>
</body>
</html>

And its iframe:

<!DOCTYPE html>
<html>
<head></head>
<style>
div {
    height: 50vh;
    width: 100%;
    background: blue;
}
</style>
<body>
    <div></div>
</body>
</html>

The important thing to note here is that both the iframe and the iframe's div element are designated as having a height of 50vh. The intended behaviour may be that the iframe honor the parent context's viewport height or width. Instead, the result looks like this:

That is, the height of the blue element is ~25% of the browser window, instead of the expected 50% (100% of the iframe). Although we may wish the iframe to respect the viewport of its parent, this example makes a good case for how unintuitive that may be, though it surely would make the v* units more valuable for content being iframe'd in. The problem has to do with how viewport height is determined.

From the spec:

The viewport-percentage lengths are relative to the size of the initial containing block. When the height or width of the initial containing block is changed, they are scaled accordingly.

Both an iframe and the browser window can be the initial containing block, as they are both valid viewports. A viewport is not limited to the browser window, but instead is defined as a window or other viewing area on the screen through which users consult a document.

An iframe creates a nested browsing context when inserted into a document, and thus has its own viewport.

So yes, this is the intended behaviour - and unfortunately there is no pure CSS workaround - however, www139 has provided an example of how this can be accomplished using JavaScript. The problem with this begins when many elements' size are controlled using v* units.



回答2:

This is an excellent question. Sadly, I haven't been able to figure out a solution in CSS but I have been able to figure out a solution in JavaScript which I think is your best bet at the moment. Remember that the frames must be on the same domain for this to work.

Hope this helps. If this answer needs improvement, please comment below :-)

Solution in Theory (can't use here on SO because of frame origin issue):

window.addEventListener('load',function(){
    initializeV();
    function initializeV(){
            //1% of the parent viewport width (same as 1vw):
            var vw = window.parent.innerWidth/100;
            //1% of the viewport height (same as 1vh):
            var vh = window.parent.innerHeight/100;

            //assign width and height to your v unit elements here
    }

    window.parent.addEventListener('resize',function(){
          //when the browser window is resized; recalculate
          initializeV();
    });
});

Edit (Dec. 2018): In the comments, I was asked to supply an example. I can't do an exact example because the codepens on Stackoverflow load over a different frame origin than the page. However, I can mimic the effect. For practical applications, please reference the code snippet above. This snippet is meant merely to illustrate how it works.

Practical Application. Uses the concept explained above but without frame reference.

window.addEventListener('load',function(){
        initializeV();
        function initializeV(){
                //note: I can't use window.parent becuase the code snippet loads on a different frame than the parent page. See the other snippet for a practical example. This snippet is meant to merely illustrate the effect.
                //1% of the parent viewport width (same as 1vw):
            	var vw = window.innerWidth/100;
                //1% of the viewport height (same as 1vh):
            	var vh = window.innerHeight/100;
        
              //this is where the magic happens. Simply set width/height/whatever to a multiple of vw/vh and add 'px'. Dimensions must be in pixels since the vw/vh measurement is based on pixels.
              document.getElementById('test').style.width = 30*vw+'px';
              document.getElementById('test').style.height = 50*vh+'px';
                //assign width and height to your v unit elements here
        }
        
        window.addEventListener('resize',function(){
              //when the browser window is resized; recalculate
              initializeV();
        });
    });
#test{
background:red;
}
<div id="test"></div>



标签: css css3 iframe