JSスクリプトを動的にロードしようとしていますが、jQueryを使用するオプションはありません。
getScript がどのように実装されているかをjQueryソースで確認し、ネイティブJSを使用してそのアプローチを使用してスクリプトをロードできるようにしました。ただし、getScriptはjQuery.get()のみを呼び出します
そして、getメソッドが実装されている場所を見つけることができませんでした。
私の質問は
ネイティブJavaScriptを使用して独自のgetScriptメソッドを実装する信頼できる方法は何ですか?
ありがとう!
次のようなスクリプトを取得できます。
(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'));
次に、コールバック機能を備えた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);
}
まず、@ 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);
});
}
使用法:
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');
});
コードは無視されます。
これを使って
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 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;
}
_
サンプル使用法:
_prefixScript("myScript1.js");
prefixScript("myScript2.js", function () { alert("The script \"myScript2.js\" has been correctly loaded."); });
_
ただし、@ Agamemnusのコメントを考慮する必要があります。onloadFunction
が呼び出されたときにスクリプトが完全にロードされない場合があります。タイマーを使用してsetTimeout(func, 0)
を使用すると、イベントループでドキュメントに追加されたスクリプトをファイナライズできます。イベントループは最終的にタイマーの背後にある関数を呼び出し、スクリプトはこの時点で使用する準備ができているはずです。
ただし、ES6のように、例外処理と成功処理の2つの関数を提供する代わりに、Promiseを返すことを検討する必要があります。 Promiseはイベントループによって処理されるため、タイマーの必要もなくなります。Promiseが処理されるまでに、スクリプトは既にイベントループによってファイナライズされています。
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!"); });
_
ここにはいくつかの優れたソリューションがありますが、多くは時代遅れです。 @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();
}
_
$.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;
}
}
_