美文网首页
iframe在顶级页面弹窗并交互

iframe在顶级页面弹窗并交互

作者: 日不落000 | 来源:发表于2022-03-10 15:35 被阅读0次

    思路如下:

    使用window.top添加元素到顶层页面,
    通过Portal将子组件渲染到该元素,
    在iframe内部可以写弹窗内的布局,
    在window.top添加样式和方法,
    在找到对应的元素设置监听onclick/oninput方法,
    然后就可以操作mobx数据,
    使顶级页面和iframe进行交互。

    相关代码如下:

    • iframeUtil.js
    
    /**
     * 在文档 iframeDocument 中添加样式
     * @param {*} iframeDocument
     * @param {*} cssText
     */
    const addCSS = (iframeDocument, cssText) => {
        var style = iframeDocument.createElement('style'), //创建一个style元素
            head = iframeDocument.head || iframeDocument.getElementsByTagName('head')[0]; //获取head元素
        style.type = 'text/css'; //这里必须显示设置style元素的type属性为text/css,否则在ie中不起作用
        if (style.styleSheet) {
            //IE
            var func = function() {
                try {
                    //防止IE中stylesheet数量超过限制而发生错误
                    style.styleSheet.cssText = cssText;
                } catch (e) {}
            };
            //如果当前styleSheet还不能用,则放到异步中则行
            if (style.styleSheet.disabled) {
                setTimeout(func, 10);
            } else {
                func();
            }
        } else {
            //w3c
            //w3c浏览器中只要创建文本节点插入到style元素中就行了
            var textNode = iframeDocument.createTextNode(cssText);
            style.appendChild(textNode);
        }
        head.appendChild(style); //把创建的style元素插入到head中
    };
    
    const addTopModal = () => {
        const topModal = document.createElement('div');
        topModal.setAttribute('id', 'topModal');
        topModal.setAttribute(
            'style',
            'position:absolute;z-index:999;width:100%;height:100%;background:rgba(0,0,0,0.5);overflow:hidden;top: 0;bottom: 0;left: 0;right: 0;'
        );
        window.top.document.body.appendChild(topModal);
    };
    
    /**
     * 添加全局的 topModal
     * 如果已存在,那么不做处理,
     * 如果不存在,那么添加。
     * @returns
     */
    const addTopModalAndCheckExist = () => {
        let topModal = window.top.document.getElementById('topModal');
        if (!topModal) {
            addTopModal();
            topModal = window.top.document.getElementById('topModal');
        }
        window.top.topModal = topModal;
        return topModal;
    };
    
    /**
     * 移除全局的 topModal
     * @returns
     */
    const removeTopModal = () => {
        if (window.top.topModal) {
            if (window.top.topModal.parentElement) {
                window.top.topModal.parentElement.removeChild(window.top.topModal);
            }
            window.top.topModal = null;
        }
    };
    
    /**
     * 初始化全局的 topModal
     * IframeServicesCatalog 相关
     * 每一个弹窗要维护自己的 init 方法
     * 内嵌iframe只需要提供关闭弹窗功能
     * 其他的内容都是可以直接展示出来的
     *
     * @returns
     */
    const initTopModalIframeServicesCatalog = () => {
        const eleClose = window.top.document.getElementById('iframeServicesCatalog-close');
        eleClose.onclick = window.top.customTopModal.onClickClose;
    };
    
    /**
     * 初始化全局的 topModal
     * TopModalForm 相关
     * 每一个弹窗要维护自己的 init 方法
     * 在顶级页面弹窗展示form表单,
     * 并设置相关监听,
     * 同步数据到mobx,
     * TopModalForm 示例添加;
     *
     *
     * @returns
     */
    const initTopModalForm = () => {
        const eleClose = window.top.document.getElementById('TopModal-close');
        eleClose.onclick = window.top.customTopModal.onClickClose;
        const btnUpdateMobxData = window.top.document.getElementById('customTopModal.btnUpdateMobxData');
        btnUpdateMobxData.onclick = window.top.customTopModal.onClickUpdateMobxData;
        const inputUpdateMobxData = window.top.document.getElementById('customTopModal.inputUpdateMobxData');
        // inputUpdateMobxData.onchange = window.top.customTopModal.onChangeInput; // 这个无效 input监听需要使用oninput
        inputUpdateMobxData.oninput = window.top.customTopModal.onChangeInput;
    };
    
    const customIframe = { addCSS };
    const customTopModal = {
        initTopModalIframeServicesCatalog,
        initTopModalForm,
        addTopModalAndCheckExist,
        removeTopModal
    };
    
    //全局调用 iframe 相关方法
    window.top.customIframe = customIframe;
    
    //全局调用 topModal 相关方法
    window.top.customTopModal = customTopModal;
    
    export { customIframe, customTopModal };
    
    • Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。
      components/topModal.js
    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    import { customTopModal } from '@/utils/iframeUtil';
    
    class TopModal extends React.Component {
        constructor(props) {
            super(props);
            customTopModal.addTopModalAndCheckExist();
            this.el = document.createElement('div');
        }
    
        componentDidMount() {
            // 在 Modal 的所有子元素被挂载后,
            // 这个 portal 元素会被嵌入到 DOM 树中,
            // 这意味着子元素将被挂载到一个分离的 DOM 节点中。
            // 如果要求子组件在挂载时可以立刻接入 DOM 树,
            // 例如衡量一个 DOM 节点,
            // 或者在后代节点中使用 ‘autoFocus’,
            // 则需添加 state 到 Modal 中,
            // 仅当 Modal 被插入 DOM 树中才能渲染子元素。
            window.top.topModal.appendChild(this.el);
        }
    
        componentWillUnmount() {
            customTopModal.removeTopModal();
        }
    
        render() {
            return ReactDOM.createPortal(this.props.children, this.el);
        }
    }
    
    export default TopModal;
    
    
    
    • stores/TopModalForm.js mobx 数据管理
    import { observable, action, runInAction } from 'mobx';
    // import { querySrvcatsWithoutChild } from '@/services/api';
    
    export default class TopModalForm {
        @observable
        data = [];
        @observable
        inputValue = '';
    
        @action.bound
        getData = async () => {
            // const res = await querySrvcatsWithoutChild();
    
            const menuNames = ['a', 'b', 'c', 'd'];
    
            runInAction(() => {
                this.data = menuNames;
            });
        };
        @action.bound
        onChangeInput = async value => {
            this.inputValue = value;
        };
        @action.bound
        onClickUpdateMobxData = async () => {
            this.inputValue = `value${this.inputValue}`;
        };
    }
    
    
    • routes/TopModalForm/customStyle.js 要添加到window.top 的样式
    
    const borderStyle = '1px solid #e6eaef';
    
    let topModal = `
    #TopModalForm {
      margin: 0 auto;
    
    }
    
    .TopModalForm-wrap {
      width: 70%;
      margin: 80px auto;
      height: 640px;
      background: #12335B;
    }
    .TopModalForm-header {
      border-bottom: ${borderStyle};
      display: flex;
      justify-content: space-between;
      align-items: center;
      font-size: 20px;
      padding: 10px;
    }
    
    
    `;
    // let isPreviewPage = ".isPreviewPage {background: red;}"
    
    let styleText = topModal;
    // +isPreviewPage
    export default styleText;
    
    
    • routes/TopModalForm/TopModalForm.js
    import React, { Component } from 'react';
    import { observer, inject } from 'mobx-react';
    import { Icon, Input, Button } from '@xxx/components';
    import TopModal from '@/components/topModal';
    import { withRouter } from 'react-router';
    import gd_icon from '../../../public/static/images/gd_icon.png';
    import { customTopModal } from '@/utils/iframeUtil';
    
    @withRouter
    @inject('topModalFormStore')
    @observer
    class TopModalForm extends Component {
        constructor(props) {
            super(props);
            // todo 需要在构造器里面初始化相关方法才能在当前页面正常使用
            window.top.customTopModal.onChangeInput = this.onChangeInput;
            window.top.customTopModal.onClickUpdateMobxData = this.onClickUpdateMobxData;
            this.state = {};
        }
    
        onClickUpdateMobxData = e => {
            console.log('onClickUpdateMobxData.e', e);
            const { inputValue } = this.props.topModalFormStore;
            console.log('onClickUpdateMobxData.inputValue', inputValue);
    
            const { onClickUpdateMobxData } = this.props.topModalFormStore;
            onClickUpdateMobxData();
        };
        onChangeInput = e => {
            console.log('onChangeInput.e', e);
            const value = e.target.value;
            const { onChangeInput } = this.props.topModalFormStore;
            onChangeInput(value);
            const { inputValue } = this.props.topModalFormStore;
            console.log('onChangeInput.inputValue', inputValue);
        };
    
        initData = async () => {
            customTopModal.initTopModalForm();
        };
    
        componentDidMount() {
            this.initData();
        }
    
        componentWillUnmount() {}
    
        render() {
            const { title, data: dataProps } = this.props;
            const { data, inputValue } = this.props.topModalFormStore;
    
            return (
                <div className="">
                    <TopModal>
                        <div className="TopModalForm-wrap">
                            <div className="TopModalForm-header">
                                <span>{title}</span>
                                <Icon type="close" id="TopModal-close" onClick={window.top.customTopModal.onClickClose} />
                            </div>
                            <div className="TopModalForm-main">
                                <div>
                                    本地图片使用示例:
                                    <img src={gd_icon} className="" alt="暂无图片"></img>
                                </div>
                                <div>
                                    props数据使用示例:
                                    {dataProps.map((item, i) => {
                                        return item.name;
                                    })}
                                </div>
                                <div>
                                    mobx数据使用示例:
                                    {data.map((item, i) => {
                                        return item;
                                    })}
                                </div>
    
                                <div>mobx数据使用示例: 输入框内容为{inputValue}</div>
    
                                <Input
                                    value={inputValue}
                                    id="customTopModal.inputUpdateMobxData"
                                    // onChange={window.top.customTopModal.onChangeInput} // 这样写是无效的 需要通过id获取元素并设置监听
                                />
    
                                <Button
                                    id="customTopModal.btnUpdateMobxData"
                                    // onClick={window.top.customTopModal.onClickUpdateMobxData} // 这样写是无效的 需要通过id获取元素并设置监听
                                >
                                    更新mobx数据
                                </Button>
                            </div>
                        </div>
                    </TopModal>
                </div>
            );
        }
    }
    
    export default TopModalForm;
    
    
    • routes/TopModalForm/TopModalFormEntry.js
    import React, { Component } from 'react';
    import { observer, inject } from 'mobx-react';
    import { withRouter } from 'react-router';
    import styleText from './customStyle';
    import TopModalForm from './TopModalForm';
    import { customIframe, customTopModal } from '@/utils/iframeUtil';
    
    @withRouter
    @inject('topModalFormStore')
    @observer
    class TopModalFormEntry extends Component {
        constructor(props) {
            super(props);
            this.state = {
                visibleTopModal: false,
                title: 'title'
            };
        }
    
        initData = async () => {
            this.props.topModalFormStore.getData();
            customIframe.addCSS(window.top.document, styleText);
            window.top.customTopModal.onClickClose = this.onClickClose;
        };
    
        componentDidMount() {
            this.initData();
        }
    
        componentWillUnmount() {}
    
        onClickClose = () => {
            console.log('onClick 关闭');
            this.setState({
                visibleTopModal: false
            });
        };
    
        showTopModal = () => {
            console.log('onClick 展示');
            this.setState({
                visibleTopModal: true
            });
        };
    
        render() {
            const { title, visibleTopModal } = this.state;
            const { data } = this.props.topModalFormStore;
    
            return (
                <div className="TopModalFormEntry">
                    <div
                        onClick={() => {
                            this.showTopModal();
                        }}
                    >
                        子级页面 TopModalForm
                        {data.map((item, i) => {
                            return item;
                        })}
                    </div>
                    {visibleTopModal && <TopModalForm title={title} data={data} />}
                </div>
            );
        }
    }
    
    export default TopModalFormEntry;
    
    

    参考链接:

    相关文章

      网友评论

          本文标题:iframe在顶级页面弹窗并交互

          本文链接:https://www.haomeiwen.com/subject/ningultx.html