admin管理员组文章数量:1134247
How can I inject a <script src="/"></script>
element into my page, wait for it to execute, and then use functions that it defines?
FYI: In my case, the script will do some credit card processing in rare cases, so I don't want to include it always. I want to include it quickly when the user opens up a change-credit-card-options dialog, and then send it the new credit card options.
Edit for additional detail: I do not have access to the remote script.
How can I inject a <script src="https://remote.com/"></script>
element into my page, wait for it to execute, and then use functions that it defines?
FYI: In my case, the script will do some credit card processing in rare cases, so I don't want to include it always. I want to include it quickly when the user opens up a change-credit-card-options dialog, and then send it the new credit card options.
Edit for additional detail: I do not have access to the remote script.
Share Improve this question edited Aug 26, 2019 at 21:58 Brian Tompsett - 汤莱恩 5,87572 gold badges61 silver badges133 bronze badges asked Dec 20, 2011 at 16:27 Riley LarkRiley Lark 20.9k15 gold badges84 silver badges129 bronze badges 2- Do you have control over the remote script? If so it might be easier to have the script itself call your code when it is done, a-la JSONP – hugomg Commented Dec 20, 2011 at 16:44
- Unfortunately I don't have control over the remote script. Editing question to reflect that~ – Riley Lark Commented Dec 20, 2011 at 18:18
7 Answers
Reset to default 169You could use Google Analytics or Facebook's method:
(function(d, script) {
script = d.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.onload = function(){
// remote script has loaded
};
script.src = 'http://www.google-analytics.com/ga.js';
d.getElementsByTagName('head')[0].appendChild(script);
}(document));
UPDATE:
Below is the new Facebook method; it relies on an existing script tag instead of <head>
:
(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)){ return; }
js = d.createElement(s); js.id = id;
js.onload = function(){
// remote script has loaded
};
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
- Replace
facebook-jssdk
with your unique script identifier to avoid it being appended more than once. - Replace the script's url with your own.
Same method using event listeners and ES2015 constructs:
function injectScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.addEventListener('load', resolve);
script.addEventListener('error', e => reject(e.error));
document.head.appendChild(script);
});
}
injectScript('https://example.com/script.js')
.then(() => {
console.log('Script loaded!');
}).catch(error => {
console.error(error);
});
This is one way to dynamically load and execute a list of scripts synchronously. You need to insert each script tag into the DOM, explicitly setting its async attribute to false:
script.async = false;
Scripts that have been injected into the DOM are executed asynchronously by default, so you have to set the async attribute to false manually to work around this.
Example
<script>
(function() {
var scriptNames = [
"https://code.jquery.com/jquery.min.js",
"example.js"
];
for (var i = 0; i < scriptNames.length; i++) {
var script = document.createElement('script');
script.src = scriptNames[i];
script.async = false; // This is required for synchronous execution
document.head.appendChild(script);
}
// jquery.min.js and example.js will be run in order and synchronously
})();
</script>
<!-- Gotcha: these two script tags may still be run before `jquery.min.js`
and `example.js` -->
<script src="example2.js"></script>
<script>/* ... */<script>
References
- There is a great article by Jake Archibald of Google about this called Deep dive into the murky waters of script loading.
- The WHATWG spec on the tag is a good and thorough description of how tags are loaded.
Dynamic import()
Using dynamic import, you can now load modules and wait for them to excute, as simply as this:
import("http://example.com/module.js").then(function(module) {
alert("module ready");
});
If the module has already been loaded and executed, it won't get loaded and executed again, but the promise returned by import
will still resolve.
Note that the file is loaded as a module, not as just a script. Modules are executed in strict mode, and they are loaded in module scope, which means variables are not automatically made global the way they are in normally loaded scripts. Use the export
keyword in a module to share a variable with other modules or scripts.
References:
- Browser support for dynamic
import
- ES modules: A cartoon deep-dive
- Dynamic
import()
something like this should do the trick:
(function() {
// Create a new script node
var script = document.createElement("script");
script.type = "text/javascript";
script.onload = function() {
// Cleanup onload handler
script.onload = null;
// do stuff with the loaded script!
}
// Add the script to the DOM
(document.getElementsByTagName( "head" )[ 0 ]).appendChild( script );
// Set the `src` to begin transport
script.src = "https://remote.com/";
})();
hope that helps! cheers.
Create a loader
You can inject the script in an orderly manner in a loader.
Beware that the execution of the dynamically loaded scripts usually comes after statically loaded scripts (i.e.<script src="My_script.js"></script>
) (the order of injection in the DOM does not guarantee the opposite):
e.g., loader.js:
function appendScript(url){
let script = document.createElement("script");
script.src = url;
script.async = false //IMPORTANT
/*Node Insertion Point*/.appendChild(script);
}
appendScript("my_script1.js");
appendScript("my_script2.js");
my_script1.js
will effectively be executed before my_script2.js
, (helpful if dependencies of my_script2.js
are in my_script1.js
)
Note it's important to have script.async = false
because dynamically loaded scripts has async = true
by default, async
does not assure you the order of loading.
Here is my adapted version, based on the answer of Frank with an additional expression to evaluate:
static async importScript(src, expressionToEvaluateAndReturn){
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.src = src;
script.addEventListener('load', (event)=>{
if(expressionToEvaluateAndReturn){
try{
let result = eval(expressionToEvaluateAndReturn);
resolve(result);
} catch(error){
reject(error);
}
} else {
resolve();
}
});
script.addEventListener('error', () => reject('Error loading script "' + src + '"'));
script.addEventListener('abort', () => reject('Script loading aborted for "' + src + '"'));
document.head.appendChild(script);
});
}
Example usage:
let d3 = await importScript('/bower_components/d3/d3.min.js','d3')
.catch(error => {
console.log(error);
throw error;
});
本文标签: javascriptInject a script tag with remote src and wait for it to executeStack Overflow
版权声明:本文标题:javascript - Inject a script tag with remote src and wait for it to execute - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736826433a1954508.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论