一、应用场景:
有时候我们需要动态创建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系列无一例外的报错,而标准浏览器却可以正常引用。