跨域 iframe 隐藏页面元素,高度自适应

技术栈

系统 A 和系统 B 均使用 vue2 

 

需求

A 中嵌入 B 系统页面,两种情况

1、去掉页面 header、footer,仅保留页面主体

2、保留 header,去掉 footer、header 中 logo、用户信息

禁用 iframe 中滚动,iframe 窗体高度与 B 中页面一致,使用浏览器滚动条

 

1 隐藏元素

方案制定

域名不同存在跨域,无法在 A 中直接操作 B 中元素,需 B 系统开发者提供支持

iframe 加载之后,给 B 发送消息,B 接收到后将元素隐藏

实现

A 系统,展示 iframe 的 vue 文件

 1 <template lang="pug">
 2 .iframe-box
 3     iframe(id="iframeInstance" name="iframeInstance" src="http://localhost:8008" scrolling="auto" frameborder="no" seamless ref="iframe" width="100%" height="600")
 4 </template>
 5 
 6 <script>
 7 export default {
 8     async mounted() {
 9         await this.$nextTick()
10         this.$refs.iframe.addEventListener('load', this.onIframeLoad) // 给 iframe 绑定监听事件
11     },
12 
13     methods: {
14         onIframeLoad() {
15             console.log('onload')
16             this.$refs.iframe.contentWindow.postMessage({ header: false }, '*') // 加载完成之后发送消息,已知目标地址时不要用*,这里的 false 和 true 对应上述需求中两种情况
17         }
18     },
19 }
20 </script>

 

B 系统,App.vue

 1 mounted () {
 2     window.addEventListener('message', (event) => {
 3         // 判断消息来源
 4         const arr = ['xxx.com', 'xxxx.com', 'xxx.cn']
 5         const flag = arr.some(item => event.origin.includes(item))
 6         if (!flag) return
 7 
 8         console.log('message', event.data)
 9 
10         // 如果两个系统不是单点登录同步登录信息,可以接收用户信息后登录
11         if (this.$store.getters.unLogin) {
12             // login
13         }
14 
15         // 隐藏 footer
16         const footer = document.querySelector('#app .footer')
17         footer.style.display = 'none'
18 
19         // 根据接收的消息隐藏对应元素
20         if (event.data.header) {
21             const logo = document.querySelector('#app .header .logo')
22             const user = document.querySelector('#app .header .user-info')
23             logo.style.display = 'none'
24             user.style.display = 'none'
25         } else {
26             const header = document.querySelector('#app .header')
27             header.style.display = 'none'
28         }
29     }, false);
30 }

 

2 高度自适应

方案

受跨域限制,B 提供高度,A 接收后调整 iframe height 属性

考虑到展示内容可能收到 tab 切换和过滤条件等影响,通过路由守卫发送消息不满足需求,采用 MutationObserver,监听根元素和子元素

实现

A系统,iframe 展示的 vue 文件

 1 <template lang="pug">
 2 .i-iframe
 3     iframe(id="iframeInstance" name="iframeInstance" src="http://localhost:8008" scrolling="no" frameborder="no" seamless ref="iframe" width="100%" :height="height")
 4 </template>
 5 
 6 <script>
 7     export default {
 8         data() {
 9             return {
10                 height: 600,
11             }
12         },
13 
14         async mounted() {
15             await this.$nextTick()
16             this.$refs.iframe.addEventListener('load', this.onIframeLoad)
17         },
18 
19         created() {
20             window.addEventListener('message', event => {
21                 const height = event.data.height || 800
22                 if(this.height === height) return
23                 this.height = height
24             }, false)
25         },
26 
27         methods: {
28             onIframeLoad() {
29                 console.log('onload')
30                 this.$refs.iframe.contentWindow.postMessage({ header: true }, '*')
31             },
32         },
33     }
34 </script>

 

B 系统,App.vue

 1 mounted () {
 2     // 可以写成个方法放 methods 里,这里为了方便展示执行时机就不改了
 3     const targetNode = document.getElementById('app');
 4     const config = { attributes: false, childList: true, subtree: true };
 5     
 6     // 加防抖,避免多次触发
 7     const callback = debounce(() => {
 8         // B 系统设置了 min-height: 100%,过滤条件变化,数据量减少时,由于展示空间足够,高度仍保持上一次的,在这里做一次初始化
 9         window.parent.postMessage({ height: undefined }, '*');
10 
11         // 渲染完成后提供最新的页面高度
12         setTimeout(() => {
13             window.parent.postMessage({ height: targetNode.offsetHeight }, '*');
14         }, 70);
15     }, 300)
16 
17     const Mutation =
18         window.MutationObserver ||
19         window.WebKitMutationObserver ||
20         window.MozMutationObserver;
21     this.observer = new Mutation(callback);
22     this.observer.observe(targetNode, config);
23 },
24 
25 beforeDestroy () {
26     if(this.observer) {
27       this.observer.disconnect()
28       this.observer = null
29     }
30 }

 

补充

此需求中 A、B 两系统使用单点登录,自动同步登录信息。如有同步登录态信息需求,可参考隐藏元素实现代码

A 传递消息时,增加用户信息。B 接收后执行登录操作

B 系统登出时,传递消息给 A 系统,A 决定跳转登录页或其他操作

 

上一篇:nginx跨域问题


下一篇:移动端软盘遮盖输入框的解决方案