jQuery.getScript alternative in native JavaScript
我正在尝试动态加载JS脚本,但使用jQuery不是一个选项。
我检查了jQuery源代码,看看如何实现getScript,以便我可以使用该方法使用本机JS加载脚本。 但是,getScript只调用jQuery.get()
而且我无法找到get方法的实现位置。
所以我的问题是,
使用本机JavaScript实现自己的getScript方法的可靠方法是什么?
谢谢!
这是一个带回调功能的jQuery getScript替代方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function getScript(source, callback) { var script = document.createElement('script'); var prior = document.getElementsByTagName('script')[0]; script.async = 1; script.onload = script.onreadystatechange = function( _, isAbort ) { if(isAbort || !script.readyState || /loaded|complete/.test(script.readyState) ) { script.onload = script.onreadystatechange = null; script = undefined; if(!isAbort && callback) setTimeout(callback, 0); } }; script.src = source; prior.parentNode.insertBefore(script, prior); } |
你可以像这样获取脚本:
1 2 3 4 5 6 | (function(document, tag) { var scriptTag = document.createElement(tag), // create a script tag firstScriptTag = document.getElementsByTagName(tag)[0]; // find the first script tag in the document scriptTag.src = 'your-script.js'; // set the source of the script to your script firstScriptTag.parentNode.insertBefore(scriptTag, firstScriptTag); // append the script to the DOM }(document, 'script')); |
首先,感谢@ Mahn的回答。我在ES6中重写了他的解决方案并承诺,如果有人需要它,我将在这里粘贴我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | const loadScript = (source, beforeEl, async = true, defer = true) => { return new Promise((resolve, reject) => { let script = document.createElement('script'); const prior = beforeEl || document.getElementsByTagName('script')[0]; script.async = async; script.defer = defer; function onloadHander(_, isAbort) { if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) { script.onload = null; script.onreadystatechange = null; script = undefined; if (isAbort) { reject(); } else { resolve(); } } } script.onload = onloadHander; script.onreadystatechange = onloadHander; script.src = source; prior.parentNode.insertBefore(script, prior); }); } |
用法:
1 2 3 4 5 6 | const scriptUrl = 'https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoad&render=explicit'; loadScript(scriptUrl).then(() => { console.log('script loaded'); }, () => { console.log('fail to load script'); }); |
并且代码是精确的。
用这个
1 2 3 4 5 | var js_script = document.createElement('script'); js_script.type ="text/javascript"; js_script.src ="http://www.example.com/script.js"; js_script.async = true; document.getElementsByTagName('head')[0].appendChild(js_script); |
Mozilla开发者网络提供了一个异步工作的示例,并且不使用HTML实现中并不存在的'onreadystatechange'(来自@ ShaneX的答案):
1 2 3 4 5 6 7 8 9 10 11 | function loadError(oError) { throw new URIError("The script" + oError.target.src +" didn't load correctly."); } function prefixScript(url, onloadFunction) { var newScript = document.createElement("script"); newScript.onerror = loadError; if (onloadFunction) { newScript.onload = onloadFunction; } document.currentScript.parentNode.insertBefore(newScript, document.currentScript); newScript.src = url; } |
样品用法:
1 2 | prefixScript("myScript1.js"); prefixScript("myScript2.js", function () { alert("The script "myScript2.js" has been correctly loaded."); }); |
但是应该考虑@Agamemnus的注释:调用
但是,也许应该考虑返回Promise而不是为异常和成功处理提供两个函数,这将是ES6方式。这也会导致不需要定时器,因为Promise由事件循环处理 - 因为在处理Promise时,脚本已经由事件循环完成。
实现包含Promises的Mozilla方法,最终代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function loadScript(url) { return new Promise(function(resolve, reject) { let newScript = document.createElement("script"); newScript.onerror = reject; newScript.onload = resolve; document.currentScript.parentNode.insertBefore(newScript, document.currentScript); newScript.src = url; }); } loadScript("test.js").then(() => { FunctionFromExportedScript(); }).catch(() => { console.log("rejected!"); }); |
这里有一些很好的解决方案,但很多都已经过时了。 @Mahn有一个很好的,但正如评论中所述,它不完全是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | function pullScript(url, callback){ pull(url, function loadReturn(data, status, xhr){ //If call returned with a good status if(status == 200){ var script = document.createElement('script'); //Instead of setting .src set .innerHTML script.innerHTML = data; document.querySelector('head').appendChild(script); } if(typeof callback != 'undefined'){ //If callback was given skip an execution frame and run callback passing relevant arguments setTimeout(function runCallback(){callback(data, status, xhr)}, 0); } }); } function pull(url, callback, method = 'GET', async = true) { //Make sure we have a good method to run method = method.toUpperCase(); if(!(method === 'GET' || method === 'POST' || method === 'HEAD')){ throw new Error('method must either be GET, POST, or HEAD'); } //Setup our request var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { // XMLHttpRequest.DONE == 4 //Once the request has completed fire the callback with relevant arguments //you should handle in your callback if it was successful or not callback(xhr.responseText, xhr.status, xhr); } }; //Open and send request xhr.open(method, url, async); xhr.send(); } |
现在我们可以替换
1 2 3 4 5 6 7 8 9 10 11 12 13 | pullScript(file1, function(data, status, xhr){ console.log(data); console.log(status); console.log(xhr); }); pullScript(file2); pull(file3, function loadReturn(data, status){ if(status == 200){ document.querySelector('#content').innerHTML = data; } } |