js - iframe更改src后,浏览器后退只能后退iframe,不能使父页面后退解决办法
现象
最近有一个项目内嵌了一个iframe,会在一定条件下动态切换其的src达到切换页面的效果。同时页面父页面中有回退按钮,直接调用的go(-1)。此时就产生了一个奇怪的现象:点击回退按钮,iframe出现了后退,而父页面没有回退。
<template>
<div class="iframe-box">
<iframe :key="url" class="iframe" :src="url"></iframe>
</div>
</template>
<script>
import ENV from '@/constant/env'
export default {
data () {
return {
path: `${ENV.mdm}`,
url: ''
}
},
watch: {
$route (to, from) {
this.setUrl()
}
},
created () {
this.setUrl()
},
destroyed () {
window.removeEventListener('message', this.postMessageFunc)
},
mounted () {
window.addEventListener('message', this.postMessageFunc)
},
methods: {
setUrl () {
this.url = `${ENV.mdm}${window.location.pathname}${window.location.search}`
},
postMessageFunc (e) {
console.log(e, '接收的数据')
if (e.data === 'goBack') {
window.removeEventListener('message', this.postMessageFun)
this.$router.go(-1)
} else if (e.data === 'workbenchApproval') {
this.$router.push('/workbench/my-approval')
} else if (e.data === 'workbenchApplication') {
this.$router.push('/workbench/my-application')
}
// let arr = [
// 'http://mdm-dev.didichuxing.com',
// 'http://mdm-test.didichuxing.com',
// 'http://mdm-stage.didichuxing.com',
// 'http://mdm.didichuxing.com',
// ];
// // 跳转地址
// let pathUrl = {
// draft: '/task/draft',
// submitted: '/task/submitted',
// home: '/home',
// todo: '/task/todo',
// done: '/task/done',
// notified: '/task/notify',
// };
// 来源判断
// if (arr.includes(e.origin)) {
// if (e.data === 'goBack') {
// window.removeEventListener('message', this.postMessageFun);
// this.$router.go(-1);
// } else {
// window.removeEventListener('message', this.postMessageFun);
// // this.$router.push({
// // path: `${pathUrl[e.data]}`,
// // query: {
// // source: 'supplier'
// // }
// // })
// }
// }
}
}
}
</script>
<style lang="less" scoped>
.iframe-box {
width: 100%;
height: 100%;
}
.iframe {
width: 100%;
height: inherit;
border: none;
}
</style>
重置iframe的高度函数
function setIframeHeight () {
//初始不执行
try {
vm.timer;
} catch (e) {
return;
}
var ifm = document.getElementById("main");
ifm.height = 0;
var iframeWin = ifm.contentWindow || ifm.contentDocument.parentWindow;
vm.timer = setInterval(function () { //开启定时器
if (iframeWin.document.body && iframeWin.$ && iframeWin.$("#dpLTE").length > 0) {
var ifmHeight = iframeWin.$("#dpLTE").height(); //iframe高度
//var ifmHeight = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight; //iframe高度
ifm.height = ifmHeight;
iframeWin.$("body").css("overflow", "hidden"); //超出隐藏
iframeWin.$(".content-fluid").css("padding", "0"); //消除边距
//window.clearInterval(vm.timer); //清除定时器
}
}, 200);
}
类似于下面这样:
解法一
找到原因后,第一想法就是直接整个替换iframe,于是单独写了一个renderIframe的方法,每次生成一个新的iframe,然而事与愿违。虚拟dom在diff过程中发现只有src变了,于是patch的时候直接更换src,最终回到了最初的起点。
浏览器的机制如此,在iframe导航变化后手动点击浏览器的后退按钮也依然只是后退iframe中的导航。
有一种解决方案是不要修改iframe.src,而是删除旧iframe元素,新建一个iframe元素并替换它,这样不会产生history。
直接createElement,替换原来的iframe。
// 写法一
const renderIframe = (src: string) => {return <iframe src={src} />
}
// 写法二
const renderIframe = (src: string) => {const iframe = document.createELement('iframe')iframe.src = srcreturn iframe
}
解法二
巧用key特性,在虚拟dom的diff算法中,key有着超高的地位,如果同一类型的虚拟节点的key不相同,就会直接销毁重新挂载,而没有key区分时就会尽可能的复用打补丁(patch)。根据这个特性,只需在iframe上增加不同的key即可,本人直接使用的src。
通过v-if来控制iframe,让iframe消失且重新加载(vue)
<iframe id="main" name="main" ref="iframe" frameborder="0" width="100%" scrolling="auto" v-if="reloadIframe" :src="iframeUrl" @load="setIframeHeight()">
methods: {
view: function() {
// 先让iframe销毁
var that = this;
that.reloadIframe = false;
that.$nextTick(function() {
// 赋值地址,再加载
that.iframeUrl = url;
that.reloadIframe = true;
})
}
}
// vue.js
<iframe :key="src" :src="src" />// react.js
<iframe key={src} src={src} />
解法三 使用document.referrer,可以获取前一页面的URL地址的方法
function back() { window.location.href = window.document.referrer; }