• 移除script标签引起的兼容性问题


    一、应用场景:

    有时候我们需要动态创建script标签实现脚本的按需加载,我们会为script标签绑定onload或者onreadystatechange事件,用于检测动态脚本是否加载并执行完毕,在事件处理程序中引用动态脚本创建的变量。

    二、问题描述:

    如果我们动态创建script标签并绑定事件处理程序后,立即移除script标签,那么事件处理程序是否会执行?如果能执行,那么能否正常引用动态脚本创建的变量?

    三、看如下程序:

    function inviteFriends() {
        var self = this;
    
        if ($("#onlineserviceMailToFriend").size() > 0) {
            showMail();
        } else {
            var jsUrl = "http://images.139cm.com/mpost2014/js/mpost/onlineservice/m2012.mpost.onlineservice.view.mailtofriend.js?v=1.0";
            
            // 动态创建script标签加载脚本
            M139.core.utilCreateScriptTag({
                id : "onlineserviceMailToFriend",
                src : jsUrl,
                charset : "utf-8"
            }, showMail);
        }
    
        // 异步请求脚本之后马上移除script标签
        $("#onlineserviceMailToFriend").remove();
    
        // script标签的onload或onreadystatechange事件处理程序
        function showMail() {
            if (!self.mailToFriend) {
                // IE系列会报错找不到对象:M2012.Mpost.OnlineService.View.MailToFirend
                // chrome浏览器可正常运行
                self.mailToFriend = new M2012.Mpost.OnlineService.View.MailToFirend({
                    model : self.model
                });
            }
            self.mailToFriend.render();
        }
    }
    
    /**
     *动态加载script标签
     *@param {Object} options 配置
     *@param {Stirng} options.id script标签的id ;
     *@param {Stirng} options.src JS文件地址(完整路径);
     *@param {Stirng} options.charset 给script标签加charset属性
     *@param {Function} callback 加载完成的回调
     *@example
     *M139.core.utilCreateScriptTag(
     {
     id:"examplejs",
     src:"http://images.139cm.com/m2012/richmail/js/example.js",
     charset:"utf-8"
     },
     function(){
     alert("文件加载完毕");
     }
     *);
     */
    function utilCreateScriptTag(options, callback) {
        var This = this;
        if (callback) {
            var _callback = callback;
            var callback = function() {
                _callback.call(This);
            }
        }
        var scriptId = options.id;
        var dataHref = this.getScriptPath(options.src);
        var charset = options.charset;
        var isReady = false;
        var head = document.getElementsByTagName("head")[0];
        var objScript = scriptId && document.getElementById(scriptId);
        //是否移出脚本DOM(非IE9时处理)
        var isRemoveScriptDom = !document.all && true || false, browserVersion = ["msie 10.0", "msie 9.0", "chrome", "firefox"], i = 0, bvLenght = browserVersion.length - 1, currVersion = window.navigator.userAgent.toLowerCase() || "";
        //IE9、chrome、firefox时处理
        while (i <= bvLenght) {
            isRemoveScriptDom = currVersion.indexOf(browserVersion[i]) > -1 && true || false;
            if (isRemoveScriptDom) {
                break;
            }
            i++;
        }
        browserVersion = null;
        try {
            if (objScript && isRemoveScriptDom) {
                objScript.src = "";
                objScript.parentNode.removeChild(objScript);
                objScript = null;
            }
        } catch (e) {
        }
        if (objScript != null) {
            if (dataHref.indexOf("?") == -1)
                dataHref += "?";
            dataHref += "&" + Math.random();
            objScript.src = dataHref;
            var dataScript = objScript;
        } else {
            var dataScript = document.createElement("script");
            if (scriptId) {
                dataScript.id = scriptId;
            }
            if (charset) {
                dataScript.charset = charset;
            }
            try {
                if (dataHref.indexOf("?") == -1) {
                    dataHref = M139.Text.Url.makeUrl(dataHref, {
                        sid : top.$App.getSid()
                    });
                }
            } catch (e) {
            }
            dataScript.src = dataHref;
            dataScript.defer = true;
            dataScript.type = "text/javascript";
            head.appendChild(dataScript);
        }
        if (document.all) {
            dataScript.onreadystatechange = function() {
                if (dataScript.readyState == "loaded" || dataScript.readyState == "complete") {
                    isReady = true;
                    if (callback)
                        callback();
                }
            }
        } else {
            dataScript.onload = function() {
                isReady = true;
                if (callback)
                    callback();
            }
            dataScript.onerror = function() {
                isReady = true;
                if (callback)
                    callback();
            }
        }
    }
    

    四、结论:

    由于函数utilCreateScriptTag是通过属性赋值的方式注入事件处理程序,而移除脚本调用的是jQuery的remove方法,dom虽然移除但事件依然存在于内存,所以当浏览器加载脚本并执行完毕,事件处理程序依然会被调用,这一点IE浏览器与标准浏览器表现一致,但是当试图在事件处理程序中引用动态脚本创建的变量时,IE系列无一例外的报错,而标准浏览器却可以正常引用。

  • 相关阅读:
    ubuntu16.04左边栏图标效果设置
    VMware虚拟机 Ubuntu 16.04 安装 VMware Tools
    微信换取openid的值
    thinkphp关于T方法
    Think关于循环的事
    base64格式转换为图片
    Think视图模型格式
    thinkphp里多表事务
    ThinkPHP数据库驱动之mysql事物回滚
    webhook是啥?
  • 原文地址:https://www.cnblogs.com/hellohuman/p/3946311.html
Copyright © 2020-2023  润新知