一、背景
公司A
想开发一个包含功能1、2、3…的网站,经过调研发现,公司B
开发的网站包含了其所需的一半功能,公司A
想直接把公司B
的网站嵌入到他们的网站中或通过一个按钮跳转到公司B
的网站,并希望对公司B
的网站做一些控制。
二、相关技术
2.1 网页嵌入
html
提供了iframe
标签用于在网页中嵌入其他网站,下面介绍几个主要的属性
2.1.1 src
用于设置嵌入网站的地址,如https://www.csdn.net
,简单的使用如下,效果如下图
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8">
</head>
<body><h2>嵌入CSDN</h2><br><iframe id="child" src='https://www.csdn.net'></iframe><script></script>
</body>
</html>
2.1.2 width和height
width
用于设置嵌入网页的宽度,height
为高度。从上图可以看出默认的范围比较小,想要嵌入的网页范围变大,可设置这两个参数,单位为像素
。如将上述的iframe
参数改为如下,可得到较大范围的嵌入网页
<iframe id="child" src='https://www.csdn.net' width="1600px" height="800px"></iframe>
2.1.3 frameborder
用于控制嵌入的网页是否有边框,1为有边框,0为无边框
。从上图中可以看出默认是带边框的。下图为无边框的效果。
2.1.4 scrolling
用于控制嵌入的网页是否有滑动条,1为有,0为无
,默认为有。
2.2 打开新网页
作用 在新的网页中打开网站
语法 window.open(URL)
URL
为打开的页面的网址。如果没有指定网址,则打开一个新的空白窗口- 返回新网页的
window
对象
2.3 网页通信
2.3.1 postMessage
作用 向其他窗口发送消息
语法 otherWindow.postMessage(message, targetOrigin, [transfer])
otherWindow
为其他窗口的一个引用,比如iframe
的contentWindow
属性message
为将要发送到其他window
的数据,为字符串targetOrigin
为能接收到消息事件的窗口,格式为窗口的网址,如果想要所有窗口都能接收到消息,可设置为*
[transfer]
为可选参数,是一串和message
同时传递的Transferable
对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。此参数一般不使用
2.3.2 addEventListener
作用 接收消息
语法 window.addEventListener(event, function, useCapture)
event
为事件名称,一般为"message"
function
为消息处理函数,用户可自定义,输入参数为e
,e
包含2个常用的参数,e.data
为接收到的字符串消息;e.origin
为发送消息的网址,可通过此参数来做判断是否需要处理,以防攻击useCapture
用来处理当存在多级组件都有接收消息响应函数时的处理顺序,true表示从外向内,即外层的先响应,false为从内向外,默认为false
2.3.3 window.opener
window.opener
为打开当前网页的网页对象,如winA.open(winB)
,则winB.opener
为winA
,使用该变量可实现被打开网页向主网页发送消息
2.4 使用window.onload来处理打开新窗口后异步执行的问题
window.onload
是一个在网页加载完成后自动执行的函数,用户可自定义该函数。打开新窗口发送消息存在的问题
:在向新窗口发送消息的案例中,由于window.open()
需要时间,这个时间跟被打开的网站的加载速度有关,而该函数的执行是异步的,即该语句后面的代码不会等待新窗口完全加载后再执行,从而导致post的消息不起作用。解决方法1
:对window.open()
后的代码使用setTimeout
函数延迟执行,此方案严重依赖于设置的延迟时长,如果设置小了,代码执行不起作用;设置大了又会有明显的迟滞,且不同网站的加载速度不同,即便相同的网站,受限于网速,打开的速度也不一样,延迟时长很难设置。所以此方法不推荐。解决方法2
:在子页面的window
中增加onload
函数,在这个函数中我们使用window.opener
来向主网页发送消息,告诉它子网页加载好了,然后主网页再发送消息给子网页进行处理,这样虽然会多一些交互代码,但可以精确地执行交互。所以推荐使用该方法,下述的案例中就使用了该方法。
三、代码案例
正常一般为网站服务,此处为了方便直接使用了html
文件,其中postMessage
的targetOrigin
就无法设置html
路径了,如果是服务的话可直接填网址,如"https://www.csdn.net"
,所以此处直接用"*"
3.1 主页面代码
<!-- main: child.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8">
</head>
<body><h2>这是主网页</h2><br><span>向内嵌网页发送的消息</span><input id="postMsg" type="text" value=""/><button onclick="handleSendMsgToCurWindow()">发送到内嵌网页</button><button onclick="handleSendMsgToNewWindow()">发送到新网页</button><iframe id="child" src='child.html' frameborder="1" scrolling="yes" style="position:fixed; top: 150px; left: 10px; width:400px; height:200px"></iframe><script>var newWindow = undefined; //子网页windowfunction handleSendMsgToCurWindow(){ let postMsg = document.getElementById("postMsg").value;if(postMsg == ""){alert("消息不能为空!");return;}// 获取内嵌网页framevar childFrame = document.getElementById('child');childFrame.contentWindow.postMessage(postMsg, '*');}function handleSendMsgToNewWindow(){ let postMsg = document.getElementById("postMsg").value;if(postMsg == ""){alert("消息不能为空!");return;}if(newWindow == undefined){// 如果子网页未打开,则打开newWindow = window.open("child.html");// 子网页打开需要时间且是异步的,需要监听子网页是否完成加载的反馈,等加载完后再发送消息window.addEventListener('message', (e)=>{ if(e.data == "childLoaded" && newWindow){let postMsg = document.getElementById("postMsg").value;newWindow.postMessage(postMsg, '*');}}, false)}else{// 如果子网页已经打开,则直接发送消息newWindow.postMessage(postMsg, '*');}}</script>
</body>
</html>
3.2 子页面代码
<!-- file: child.html -->
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8">
</head>
<body><h2>这是内嵌网页</h2><br><span>从主网页收到的消息</span><input id="recvMsg" type="text" value=""/><script>window.addEventListener('message', (e)=>{console.log(e);document.getElementById("recvMsg").value = e.data;}, false)// 网页加载完后向父窗口发送加载完成的消息window.onload = ()=>{if(window.opener){window.opener.postMessage("childLoaded", "*");}}</script>
</body>
</html>