原生 JavaScript 中的 jQuery.getScript 替代方案
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16839698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery.getScript alternative in native JavaScript
提问by ILikeTacos
I'm trying to load JS scripts dynamically, but using jQuery is not an option.
我正在尝试动态加载 JS 脚本,但不能使用 jQuery。
I checked jQuery source to see how getScriptwas implemented so that I could use that approach to load scripts using native JS. However, getScript only calls jQuery.get()
我检查了 jQuery 源代码以了解getScript是如何实现的,以便我可以使用该方法使用本机 JS 加载脚本。但是,getScript 只调用 jQuery.get()
and I haven't been able to find where the get method is implemented.
我一直无法找到 get 方法的实现位置。
So my question is,
所以我的问题是,
What's a reliable way to implement my own getScript method using native JavaScript?
使用本机 JavaScript 实现我自己的 getScript 方法的可靠方法是什么?
Thanks!
谢谢!
采纳答案by Mathletics
You can fetch scripts like this:
您可以像这样获取脚本:
(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'));
回答by Mahn
Here's a jQuery getScript alternative with callback functionality:
这是具有回调功能的 jQuery getScript 替代方案:
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);
}
回答by AXE
Firstly, Thanks for @Mahn's answer. I rewrote his solution in ES6 and promise, in case someone need it, I will just paste my code here:
首先,感谢@Mahn 的回答。我在 ES6 中重写了他的解决方案并承诺,如果有人需要它,我会在这里粘贴我的代码:
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);
});
}
Usage:
用法:
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');
});
and code is eslinted.
和代码是 eslinted。
回答by Rohit Agrawal
use this
用这个
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);
回答by m.w.
Mozilla Developer Network provides an examplethat works asynchronously and does not use 'onreadystatechange' (from @ShaneX's answer) that is not really present in a HTMLScriptTag:
Mozilla Developer Network 提供了一个异步工作的示例,并且不使用 HTMLScriptTag 中并不真正存在的“onreadystatechange”(来自@ShaneX 的回答):
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;
}
Sample usage:
示例用法:
prefixScript("myScript1.js");
prefixScript("myScript2.js", function () { alert("The script \"myScript2.js\" has been correctly loaded."); });
But @Agamemnus' comment should be considered: The script might not be fully loaded when onloadFunction
is called. A timer could be used setTimeout(func, 0)
to let the event loop finalize the added script to the document. The event loop finally calls the function behind the timer and the script should be ready to use at this point.
但应考虑@Agamemnus 的评论:onloadFunction
调用时脚本可能未完全加载。计时器可用于setTimeout(func, 0)
让事件循环完成添加到文档的脚本。事件循环最终调用了定时器背后的函数,此时脚本应该可以使用了。
However, maybe one should consider returning a Promise instead of providing two functions for exception & success handling, that would be the ES6 way. This would also render the need for a timer unnecessary, because Promises are handled by the event loop - becuase by the time the Promise is handled, the script was already finalized by the event loop.
然而,也许应该考虑返回一个 Promise 而不是提供两个用于异常和成功处理的函数,这将是 ES6 的方式。这也使得不需要计时器,因为 Promise 由事件循环处理 - 因为在处理 Promise 时,脚本已经由事件循环完成。
Implementing Mozilla's method including Promises, the final code looks like this:
实现包括 Promises 在内的 Mozilla 方法,最终代码如下所示:
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!"); });
回答by Xandor
There are some good solutions here but many are outdated. There is a good oneby @Mahn but as stated in a comment it is not exactly a replacement for $.getScript()
as the callback does not receive data. I had already written my own function for a replacement for $.get()
and landed here when I need it to work for a script. I was able to use @Mahn's solution and modify it a bit along with my current $.get()
replacement and come up with something that works well and is simple to implement.
这里有一些很好的解决方案,但许多已经过时了。@Mahn有一个很好的,但正如评论中所述,它并不完全是替代品,$.getScript()
因为回调不接收数据。我已经编写了自己的函数来替代$.get()
并在需要它为脚本工作时登陆这里。我能够使用@Mahn 的解决方案并与我当前的$.get()
替代品一起对其进行一些修改,并提出了一些运行良好且易于实现的方法。
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();
}
Now we have a replacement for $.get()
and $.getScript()
that work just as simply:
现在我们有了一个替代品$.get()
,$.getScript()
它的工作方式同样简单:
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;
}
}
回答by paulcol.
This polishes up previous ES6 solutions and will work in all modern browsers
这完善了以前的 ES6 解决方案,并将适用于所有现代浏览器
Load and Get Script as a Promise
加载和获取脚本作为 Promise
const getScript = url => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
document.head.appendChild(script)
})
Usage
用法
getScript('https://dummyjs.com/js')
.then(() => {
console.log('Loaded', dummy.text())
})
.catch(() => {
console.error('Could not load script')
})
Also works for JSONP endpoints
也适用于 JSONP 端点
const callbackName = `_${Date.now()}`
getScript('http://example.com/jsonp?callback=' + callbackName)
.then(() => {
const data = window[callbackName];
console.log('Loaded', data)
})
Also, please be careful with some of the AJAX solutions listed as they are bound to the CORS policy in modern browsers https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
另外,请注意列出的一些 AJAX 解决方案,因为它们绑定到现代浏览器中的 CORS 策略https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS