script要素.onloadイベントで読み込み完了を取得する方法
というわけで、
前々回のasyncなscript要素で読み込むときの問題点
https://qiita.com/toshirot/items/d87a408f53c0461914a4
前回の読み込むライブラリファイルに依存コードを同梱する方法
https://qiita.com/toshirot/items/c60d987378ff6bdbaf27
に続き、今回はasyncなscript要素もscript要素.onloadイベントで読み込み完了を取得できそうだよね、という点を試してみたいと思います。
これができても、前回のライブラリファイルに依存コードを同梱するとコードの切り分けできて管理が楽になるというメリットはあるかなとは思います。
今回の対策
asyncなscript要素のonloadイベントで読み込み終了をキャッチしてみるというものです。
コード
main.html
今回は、script要素に id="async_script" という名前を付けてて、その後async_script.onload関数を書いてあります。id名は何でも構いませんし スクリプトで要素を呼び出せるなら classでも良いです。
<script>
let i=0
</script>
<script id="async_script" async src="js/jquery-3.6.1.min-1.js"></script>
<script>
// script要素を特定する
let async_script=document.getElementById('async_script')
// async_script load
async_script.addEventListener('load', function() {
console.log(++i,': async_script loaded')
});
// DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log(++i,': DOMContentLoaded')
});
// window.onload
window.onload = function() {
console.log(++i,': window.onload ')
}
// jqueryの $(function(){})
$(function(){
console.log(++i,': $(function(){})')
})
</script>
jquery-3.6.1.min_some.js
jqueryファイルの最後にコードを追記します。
//省略
:
:
&define.amd&&define("jquery",[],function(){return S});var Yt=C.jQuery,Qt=C.$;return S.noConflict=function(e){return C.$===S&&(C.$=Qt),e&&C.jQuery===S&&(C.jQuery=Yt),S},"undefined"==typeof e&&(C.jQuery=C.$=S),S});
// msg of at the jquery file end
console.log(++i,':(in jqf) at the jquery file end')
// DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
console.log(++i,':(in jqf) DOMContentLoaded')
});
// window.onload
window.onload = function() {
console.log(++i,':(in jqf) window.onload ')
}
// jqueryの $(function(){})
$(function(){
console.log(++i,':(in jqf) $(function(){})')
})
結果
WindowsPC(Chrome105)で試した結果
Uncaught ReferenceError: $ is not defined
1 ':(in jqf) at the jquery file end'
2 ': async_script.onload'
3 ': DOMContentLoaded'
4 ':(in jqf) DOMContentLoaded'
5 ':(in jqf) window.onload '
6 ':(in jqf) $(function(){})'
MacBook(Chrome105)で試した結果
Uncaught ReferenceError: $ is not defined
1 ': DOMContentLoaded'
2 ':(in jqf) at the jquery file end'
3 ': async_script.onload'
4 ':(in jqf) window.onload '
5 ':(in jqf) $(function(){})'
少し興味深いのは、WindowsPCでは、DOMContentLoadedがmain.html内とライブラリファイル内の2度発火していますが、MacBookでは、main.html内のみで(最初に読み込まれた1度だけ)発火していることです。
これをみても、asyncのscript要素で読み込まれたjqueryコードをDOMContentLoadedで動かすのはリスクがあると言えます。
さて、「2 ': async_script.onload'」は無事、Win/Macともに「1 ':(in jqf) at the jquery file end'」の後で発火していることが確認できました。
これは、addEventListenerを使い下記のように書いても同じです。
async_script.addEventListener('load', function() {
console.log(++i,': async_script loaded')
});
追記:コメントで@junerさんに「window.onload は プロパティなので上書きしてしまったら意味がないのでは」とご指摘頂いたので追記しておきます。
上記結果の通り、window.onload は上書きによって、片方のwindow.onloadだけが有効になっています。しかもWin/Macでmain内とライブラリ内違う場所が発火しています。つまり、DOMContentLoadedだけではなく、window.onloadにも非同期結果のタイミングにより発火位置が変わっているようです。
もちろん、@junerさんの指摘されたaddEventListenerを使えば両方で発火します。例えば
// window addEventListener onload
window.addEventListener('load', function() {
console.log(++i,':(in jqf) window addEventListener load')
});
これを、両ファイルに追記した結果はこうなります。
Uncaught ReferenceError: $ is not defined
1 ':(in jqf) at the jquery file end'
2 ': async_script loaded'
3 ': DOMContentLoaded'
4 ':(in jqf) DOMContentLoaded'
5 ': window.onload '
6 ': window addEventListener load'
7 ':(in jqf) window addEventListener load'
8 ':(in jqf) $(function(){})'
いずれにしても、「1 ':(in jqf) at the jquery file end'」後にすべての(in jqf)が発火している点は確認できました。。
以上。
ああ、そういえば、ライブラリ内の同梱コードは順次処理されるのでload系のイベントは冗長かなと思ってますがどうでしょう?
関連
前々回の、asyncなscript要素で読み込むときの問題点
https://qiita.com/toshirot/items/d87a408f53c0461914a4
前回の、読み込むライブラリファイルに依存コードを同梱する方法
https://qiita.com/toshirot/items/c60d987378ff6bdbaf27
メモ
DOMContentLoaded周りの処理を詳しく調べてみました https://qiita.com/mamosan/items/ff336b5cc0a1a95e03a7 #Qiita @MamosanDesignより