iframe自适应高度
参考文章 - iframe
前言:
首先要知道iframe的基本概念,iframe就是一个可以引入其他网页的框架,可以进行下面的设置:
<iframe src="http://www.baidu.com" frameborder="0" width="500" height="300" scrolling="no"></iframe>
这里定义了iframe的src、宽、高,并且使得iframe不能scroll。
其次,window.top对象指向的是浏览器最顶层的窗口。 window.top对象指向的是当前窗口,可能是最顶层窗口,也可能是其中的一个框架。我们可以使用window.top.location属性来得到最顶层窗口的的url。
最后,要知道iframe的高度是不能自适应高度的,其高度和宽度没有固定的规律, 所以我们这里需要解决的问题就是 iframe 的高度动态适应的问题。
实现:
希望实现高度自适应,我们根据iframe的一些特性,分为下面的几种情况:
- 同域实现高度自适应。 如 www.a.com/a.html 与 www.a.html/b.html ,这就是同域的情况。
- 跨域实现高度自适应。
- 跨子域实现。 如first.a.com/a.thml 与 second.a.com/b.html, 这就是跨子域的情况。
- 完全跨域实现。 如www.a.com/a.html 与 www.b.com/b.html , 这就是完全跨域的情况。
个人理解: 同域是同域名,而同源是同域名、端口号、协议,两个是不同的。
同域实现高度自适应
方法1:父级页面获取子级页面的高度 给元素设置高度
这方法是用在父级页面里的,通过获取子级页面的高度给iframe设置高度
比如a.html中以iframe的形式嵌入了b.html,希望b.html可以是自适应的高度。
b.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>b</title> <style> * { margin: 0; padding: 0; } body { background-color: #ccc; } </style> </head> <body> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> <h2>这是b页面</h2> </body> </html>
a.html 如下所示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>a</title> </head> <body> <p>这是a页面</p> <iframe src="./b.html" frameborder="1"></iframe> <script> var ifr = document.querySelector('iframe'); ifr.onload = function () { var oHeight = Math.max(ifr.contentWindow.document.documentElement.offsetHeight, ifr.contentWindow.document.body.offsetHeight); var cHeight = Math.max(ifr.contentWindow.document.documentElement.clientHeight, ifr.contentWindow.document.body.clientHeight); var height = Math.max(oHeight, cHeight); ifr.style.height = height + 'px' } </script> </body> </html>
即通过ifr.contentWindow获取到window, 而document是window的属性,所以我们就可以通过window.document获取到了,后面的也就简单了。
方法二:子级页面给父级页面元素设置高度
又因为这是没有跨域存在的,所以iframe也可以通过 window.parent 来访问父document。 比如c中有iframe的d,希望d可以自适应,那么c的代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>c</title> </head> <body> <p>这是c页面</p> <iframe src="./d.html" frameborder="1"></iframe> </body> </html>
d.html 如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>d</title> <style> * { margin: 0; padding: 0; } body { background-color: #ccc; } </style> <script> window.onload = function () { var parentDoc = window.parent.document; var oHeight = Math.max(document.documentElement.offsetHeight, document.body.offsetHeight); var cHeight = Math.max(document.documentElement.clientHeight, document.body.clientHeight); var height = Math.max(oHeight, cHeight); parentDoc.querySelector('iframe').style.height = height + 'px'; } </script> </head> <body> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> <h2>这是d页面</h2> </body> </html>
即我们通过window.parent就可以访问到父window,然后进而就可以访问到document了,那么document这个dom结构的任何东西我们就都可以访问的到了。
这些是同域的情况,那么跨域的情况呢?
跨域实现高度自适应 --- 跨子域的高度自适应
对于跨子域而言,实际上就非常简单了,我们可以直接通过设置 document.domain ,进而就可以使用之前的没有跨域的方式进行通信了,这里不做过多的赘述。
比如 'a.jd.com/3.html' 嵌入了 'b.jd.com/4.html',这种跨子域的页面。
3.html
<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>1.html</title> <script type="text/javascript"> document.domain = 'jd.com' </script> </head> <body> <iframe id="ifr" src="b.jd.com/4.html" frameborder="0" width="100%"></iframe> </body>
4.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>2.html</title> <script type="text/javascript"> document.domain = 'jd.com' </script> </head> <body> <p>这是一个ifrmae,嵌入在3.html里 </p> <p>根据自身内容调整高度</p> <p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p> <script> // 计算页面的实际高度,iframe自适应会用到 function calcPageHeight(doc) { var cHeight = Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) var sHeight = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight) var height = Math.max(cHeight, sHeight) return height } window.onload = function() { var height = calcPageHeight(document) parent.document.getElementById('ifr').style.height = height + 'px' } </script> </body> </html>
注意: 不难发现,和同域相比,这里只要在两个html中添加相同的 document.domain 就可以了。其他没有区别。
跨域实现高度自适应 --- 完全跨域的高度自适应
首先大概说一下思路,比如我们需要在localhost: 8081/a.html这个html文件中添加localhost:8088/b.html这个html文件,希望让跨域的b.html文件高度自适应,由于跨域,所以我们没有办法直接获取到localhost: 8088这个域下的文件, 但是我们可以在b.html中引入一个 localhost: 8081/c.html,通过c.html,我们就可以利用parent.parent访问到a了,这样,就可以来设置a中b的高度了。但是在c.html中怎么才能拿到b.html的高度呢? 显然这又是跨域的,不能拿到,但是,我们可以通过在iframe引入c的时候,在后面添加一个查询字符串,这样,c就可以通过location来拿到高度,通过parent.parent进行设置了,这并不会对页面产生什么影响。
分别有以下资源:
- 页面 A:http://snandy.github.io/lib/iframe/A.html
- 页面 B:http://snandy.github.io/lib/iframe/B.html
- 页面 C:http://snandy.jd-app.com
- D.js:http://snandy.github.io/lib/iframe/D.js
这四个资源的关系如下所示:
- A中 嵌入了 C, 且A和C是跨域的。
- C中 嵌入了 B, 虽然C和B不同域,但A和B是同域的。
- C中 嵌入了 D.js, D.js 放在了和A同域的项目里。
我们通过一个间接的方式,即通过一个隐藏的B.html来实现高度自适应。
但其实整体上的思路还是比较容易理解的,就是A中含有iframe的C,但是需要一个和A同域的B在C中。 在C中需要引入iframeB, 并且在c中不断地计算自己的高度,然后设置B的src添加一个高度值,B拿到这个高度值之后,通过parent.parent来修改C,这样就可以做到完全跨域的高度自适应了。
A.html的代码如下(其中嵌入了C,注意: AC不同域):
<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>A.html</title> </head> <body> <iframe id="ifr" src="http://snandy.jd-app.com" frameborder="0" width="100%"></iframe> </body> </html>
B.html代码如下(B是嵌在C的页面中的,注意:BA同域):
注意: B是隐藏的,由于他和A同域,所以可以通过parent.parent访问到A,再改变A中iframe的高度,这是最关键的,因为AB同域,所以B可以访问到A的文档对象。
<!DOCTYPE html> <html> <head> <meta charset='utf-8' /> <title>B.html</title> </head> <body> <script type="text/javascript"> window.onload = function() { var isSet = false var inteval = setInterval(function() { var search = location.search.replace('?', '') if (isSet) { clearInterval(inteval) return } if (search) { var height = search.split('=')[1] var doc = parent.parent.document var ifr = doc.getElementById('ifr') ifr.style.height = height + 'px' isSet = true } }, 500) } </script> </body> </html>
C.html (C嵌入在A中,和A不同域,要实现C的自适应,C多高那么A的iframe就有多高,C中嵌入了B.html和D.js)
<!doctype html> <html> <head> <title>C.html</title> <meta charset="utf-8"> </head> <body> <h3>这是一个很长的页面,我要做跨域iframe的高度自适应</h3> <ul> <li>页面 A:http://snandy.github.io/lib/iframe/A.html</li> <li>页面 B:http://snandy.github.io/lib/iframe/B.html</li> <li>页面 C:http://snandy.jd-app.com</li> <li>D.js:http://snandy.github.io/lib/iframe/D.js</li> </ul> <p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p> <iframe id="myifr" style="display:none" src="http://snandy.github.io/lib/iframe/B.html"></iframe> <script type="text/javascript" src="http://snandy.github.io/lib/iframe/D.js"></script> </body> </html>
D.js (在页面C载入后计算其高度,然后将计算出的height赋值给C里引入的iframe(B.html)的src)
// 计算页面的实际高度,iframe自适应会用到 function calcPageHeight(doc) { var cHeight = Math.max(doc.body.clientHeight, doc.documentElement.clientHeight) var sHeight = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight) var height = Math.max(cHeight, sHeight) return height } window.onload = function() { var doc = document var height = calcPageHeight(doc) var myifr = doc.getElementById('myifr') if (myifr) { myifr.src = 'http://snandy.github.io/lib/iframe/B.html?height=' + height // console.log(doc.documentElement.scrollHeight) } };
这里的D.js为什么要单独拿出来和AB同源呢? 不能直接把其中的代码放进去吗?
iframe使用场景
iframe优缺点
<!DOCTYPE html>
<html>
<head>
<meta charset=
"utf-8"
>
<title>2.html</title>
<script type=
"text/javascript"
>
document.domain =
'jd.com'
</script>
</head>
<body>
<p>这是一个ifrmae,嵌入在3.html里 </p>
<p>根据自身内容调整高度</p>
<p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p><p>a</p>
<script>
// 计算页面的实际高度,iframe自适应会用到
function
calcPageHeight(doc) {
var
cHeight = Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
var
sHeight = Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight)
var
height = Math.max(cHeight, sHeight)
return
height
}
window.onload =
function
() {
var
height = calcPageHeight(document)
parent.document.getElementById(
'ifr'
).style.height = height +
'px'
}
</script>
</body>
</html>