現在実行中のJavaScriptをロードしたスクリプト要素を参照するにはどうすればよいですか?
状況は次のとおりです。最初にHEADタグの下に読み込まれる「マスター」スクリプトがあります。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>
「scripts.js」には、他のスクリプトをオンデマンドでロードできるようにする必要があるスクリプトがあります。 HEAD要素のレンダリングが完了していないため、HEADタグを参照せずに新しいスクリプトを追加する必要があるため、通常の方法ではうまくいきません。
document.getElementsByTagName('head')[0].appendChild(v);
私がやりたいのは、現在のスクリプトをロードしたスクリプト要素を参照して、動的にロードされた新しいスクリプトタグをDOMの後に追加できるようにすることです。
<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>
document.currentScript
を使用しますdocument.currentScript
は、スクリプトが現在処理されている<script>
要素を返します。
<script>
var me = document.currentScript;
</script>
defer
&async
)で動作します<script type="module">
では機能しませんスクリプトにid属性を与えると、 document.getElementById()
を使用して内部からidで簡単に選択できます。
<script id="myscript">
var me = document.getElementById('myscript');
</script>
defer
&async
)で動作しますid
属性は、一部のEdgeの場合、一部のブラウザーでスクリプトの動作がおかしいdata-*
属性を使用してスクリプトを選択しますスクリプトに data-*
属性を指定すると、内部から簡単に選択できます。
<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>
これには、以前のオプションに比べて利点がほとんどありません。
defer
&async
)で動作しますquerySelector()
はすべてのブラウザーに準拠していませんid
属性を使用するよりも広くサポートされていませんid
Edgeケースで<script>
を回避します。データ属性を使用する代わりに、セレクタを使用してソースごとにスクリプトを選択できます。
<script src="//example.com/embed.js"></script>
Embed.jsで:
var me = document.querySelector('script[src="//example.com/embed.js"]');
defer
&async
)で動作しますid
属性を使用するよりも広くサポートされていませんまた、すべてのスクリプト要素をループして、それぞれを個別にチェックして、必要な要素を選択することもできます。
<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
if( isMe(scripts[i])){
me = scripts[i];
}
}
</script>
これにより、属性でquerySelector()
を十分にサポートしていない古いブラウザーで以前の両方の手法を使用できます。例えば:
function isMe(scriptElem){
return scriptElem.getAttribute('src') === "//example.com/embed.js";
}
これは、どのアプローチをとっても利点と問題を継承しますが、querySelector()
に依存しないため、古いブラウザーでも機能します。
スクリプトは連続して実行されるため、最後のスクリプト要素は現在実行中のスクリプトであることが非常に多くなります。
<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>
defer
&async
)で動作しますかスクリプトは連続して実行されるため、現在実行されているスクリプトタグは常にそれまでのページの最後のスクリプトタグです。したがって、スクリプトタグを取得するには、次のようにします。
var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];
おそらく最も簡単なことは、スクリプトタグにid
属性を指定することです。
スクリプトは、「遅延」属性も「非同期」属性もない場合にのみ順次実行されます。スクリプトタグの可能なID/SRC/TITLE属性の1つを知ることは、それらのケースでも機能する可能性があります。したがって、GregとJustinの両方の提案は正しいです。
WHATWGリストにはdocument.currentScript
の提案が既にあります。
EDIT:Firefox> 4はすでにこの非常に便利なプロパティを実装していますが、前回チェックしたIE11では使用できず、Chrome 29およびSafari 8でのみ使用できます。
EDIT:「document.scripts」コレクションについて言及した人はいませんでしたが、現在実行中のスクリプトを取得するためのクロスブラウザーの代替手段として、次のものが適していると思います。
var me = document.scripts[document.scripts.length -1];
document.CurrentScript
が存在する場合はそれを活用し、IDによるスクリプトの検索にフォールバックするポリフィルを少し紹介します。
<script id="uniqueScriptId">
(function () {
var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');
// your code referencing thisScript here
());
</script>
これをすべてのスクリプトタグの先頭に含めると、どのスクリプトタグが起動されているかを一貫して知ることができ、非同期コールバックのコンテキストでスクリプトタグを参照することもできると思います。
テストされていないので、他の人に試してみてください。
ページの読み込み時と、JavaScriptタグ(例:ajax)でスクリプトタグが追加されたときに機能する必要があります。
<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>
非同期および遅延スクリプトを処理する方法は、onloadハンドラーを活用することです。すべてのスクリプトタグに対してonloadハンドラーを設定し、最初に実行するタグを自分のものにします。
function getCurrentScript(callback) {
if (document.currentScript) {
callback(document.currentScript);
return;
}
var scripts = document.scripts;
function onLoad() {
for (var i = 0; i < scripts.length; ++i) {
scripts[i].removeEventListener('load', onLoad, false);
}
callback(event.target);
}
for (var i = 0; i < scripts.length; ++i) {
scripts[i].addEventListener('load', onLoad, false);
}
}
getCurrentScript(function(currentScript) {
window.console.log(currentScript.src);
});
スクリプトを取得するには、現在使用しているスクリプトをロードしている
var thisScript = document.currentScript;
スクリプトの先頭に参照を保持する必要があるため、後で呼び出すことができます
var url = thisScript.src
次の簡単な手順に従って、現在実行中のスクリプトブロックへの参照を取得します。
例(ABCDE345678は一意のID):
<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>
ところで、あなたの場合、古い形式のdocument.write()メソッドを使用して別のスクリプトを含めることができます。 DOMがまだレンダリングされていないことを述べたように、ブラウザーは常に線形シーケンスでスクリプトを実行するという事実を活用できます(後でレンダリングされる遅延スクリプトを除く)。したがって、ドキュメントの残りの部分はまだ「存在しません」。 document.write()を介して書き込むものはすべて、呼び出し元スクリプトの直後に配置されます。
元のHTMLページの例:
<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>
script.jsのコンテンツ:
document.write('<script src="inserted.js"></script>');
レンダリング後、DOM構造は次のようになります。
HEAD
SCRIPT script.js
SCRIPT inserted.js
SCRIPT otherscript.js
BODY
このアルゴリズムを検討してください。スクリプトがロードされたら(複数の同一のスクリプトがある場合)、document.scriptsを調べ、正しい「src」属性を持つ最初のスクリプトを見つけて保存し、データ属性または一意のclassNameで「訪問済み」としてマークします。
次のスクリプトが読み込まれたら、document.scriptsを再度スキャンし、既に訪問済みとしてマークされているスクリプトを渡します。そのスクリプトの最初の未訪問のインスタンスを取得します。
これは、同一のスクリプトがロードされた順序で、頭から体、上から下、同期から非同期に実行される可能性が高いことを前提としています。
(function () {
var scripts = document.scripts;
// Scan for this data-* attribute
var dataAttr = 'data-your-attribute-here';
var i = 0;
var script;
while (i < scripts.length) {
script = scripts[i];
if (/your_script_here\.js/i.test(script.src)
&& !script.hasAttribute(dataAttr)) {
// A good match will break the loop before
// script is set to null.
break;
}
// If we exit the loop through a while condition failure,
// a check for null will reveal there are no matches.
script = null;
++i;
}
/**
* This specific your_script_here.js script tag.
* @type {Element|Node}
*/
var yourScriptVariable = null;
// Mark the script an pass it on.
if (script) {
script.setAttribute(dataAttr, '');
yourScriptVariable = script;
}
})();
これにより、特別な属性でマークされていない最初に一致するスクリプトのすべてのスクリプトがスキャンされます。
次に、そのノードが見つかった場合は、その後のスキャンで選択されないようにデータ属性でマークします。これは、ノードが再訪問を防ぐために「訪問済み」とマークされる可能性があるグラフトラバーサルBFSおよびDFSアルゴリズムに似ています。
次のコードは、最も一貫性があり、パフォーマンスが高く、シンプルであることがわかりました。
var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
thisScript = scripts[i];
break;
}
}
console.log(thisScript);
スクリプトのファイル名を推測できる場合は、それを見つけることができます。これまでのところ、Firefoxで次の機能を実際にテストしただけです。
function findMe(tag, attr, file) {
var tags = document.getElementsByTagName(tag);
var r = new RegExp(file + '$');
for (var i = 0;i < tags.length;i++) {
if (r.exec(tags[i][attr])) {
return tags[i][attr];
}
}
};
var element = findMe('script', 'src', 'scripts.js');
スクリプトの名前(または少なくともその一部)を知っている場合、別の簡単な方法があります。
document.querySelector('script[src*="<scriptname>.js"]')
これはIE11でも機能します。