[解決済み] jQueryの$.ready()に相当する純粋なJavaScript - ページ/DOMの準備ができたときに関数を呼び出す方法 [重複]。
質問
jQueryでは、誰もが知っている素晴らしい
.ready()
という関数があります。
$('document').ready(function(){});
しかし、ライブラリの裏付けがない標準的なJavaScriptで書かれた関数を、ページが処理できるようになったらすぐに起動させたいとします。この場合、どのような方法でアプローチするのが適切でしょうか?
できることは知っています。
window.onload="myFunction()";
あるいは
body
タグを使用します。
<body onload="myFunction()">
あるいは、ページの一番下、すべてのものの後、しかし最後に試すこともできます。
body
または
html
のようなタグを使用します。
<script type="text/javascript">
myFunction();
</script>
クロスブラウザ(新旧)対応で、jQueryのように1つ以上の関数を発行する方法はありますか?
$.ready()
?
解決方法は?
クロスブラウザ対応をしてくれるフレームワークがない場合、最も簡単な方法は、ボディの最後にコードの呼び出しを記述することです。 これは
onload
なぜなら、これは DOM が準備できるのを待つだけで、すべての画像が読み込まれるのを待つわけではないからです。 そして、これはどのブラウザーでも動作する。
<!doctype html>
<html>
<head>
</head>
<body>
Your HTML here
<script>
// self executing function here
(function() {
// your page initialization code here
// the DOM will be available here
})();
</script>
</body>
</html>
モダンブラウザ(IE9以降、Chrome、Firefox、Safariの全バージョン)において、jQueryのような実装をしたい場合
$(document).ready()
メソッドをどこからでも(呼び出し側のスクリプトの位置を気にせずに)呼び出せるようにするには、次のようなものを使用すればよいでしょう。
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
使用方法
docReady(function() {
// DOM is loaded and ready for manipulation here
});
完全なクロスブラウザ互換性(IEの古いバージョンも含む)が必要で、かつ
window.onload
のようなフレームワークがどのように実装されているかを見てみる必要があるかもしれません。
$(document).ready()
メソッドです。 ブラウザの機能によって、かなり複雑なんだ。
jQueryが何をするのか、少し考えてみましょう(scriptタグが置かれている場所ならどこでも動作します)。
対応していれば、標準を試します。
document.addEventListener('DOMContentLoaded', fn, false);
にフォールバックしています。
window.addEventListener('load', fn, false )
を使用するか、IEの古いバージョンでは、それを使用します。
document.attachEvent("onreadystatechange", fn);
にフォールバックしています。
window.attachEvent("onload", fn);
それと、IEのコードパスには、よくわからない回避策があるのですが、フレームと関係があるようです。
以下は、jQueryの完全な代用品です。
.ready()
プレーンなjavascriptで書かれています。
(function(funcName, baseObj) {
// The public function name defaults to window.docReady
// but you can pass in your own object and own function name and those will be used
// if you want to put them in a different namespace
funcName = funcName || "docReady";
baseObj = baseObj || window;
var readyList = [];
var readyFired = false;
var readyEventHandlersInstalled = false;
// call this when the document is ready
// this function protects itself against being called more than once
function ready() {
if (!readyFired) {
// this must be set to true before we start calling callbacks
readyFired = true;
for (var i = 0; i < readyList.length; i++) {
// if a callback here happens to add new ready handlers,
// the docReady() function will see that it already fired
// and will schedule the callback to run right after
// this event loop finishes so all handlers will still execute
// in order and no new ones will be added to the readyList
// while we are processing the list
readyList[i].fn.call(window, readyList[i].ctx);
}
// allow any closures held by these functions to free
readyList = [];
}
}
function readyStateChange() {
if ( document.readyState === "complete" ) {
ready();
}
}
// This is the one public interface
// docReady(fn, context);
// the context argument is optional - if present, it will be passed
// as an argument to the callback
baseObj[funcName] = function(callback, context) {
if (typeof callback !== "function") {
throw new TypeError("callback for docReady(fn) must be a function");
}
// if ready has already fired, then just schedule the callback
// to fire asynchronously, but right away
if (readyFired) {
setTimeout(function() {callback(context);}, 1);
return;
} else {
// add the function and context to the list
readyList.push({fn: callback, ctx: context});
}
// if document already ready to go, schedule the ready function to run
if (document.readyState === "complete") {
setTimeout(ready, 1);
} else if (!readyEventHandlersInstalled) {
// otherwise if we don't have event handlers installed, install them
if (document.addEventListener) {
// first choice is DOMContentLoaded event
document.addEventListener("DOMContentLoaded", ready, false);
// backup is window load event
window.addEventListener("load", ready, false);
} else {
// must be IE
document.attachEvent("onreadystatechange", readyStateChange);
window.attachEvent("onload", ready);
}
readyEventHandlersInstalled = true;
}
}
})("docReady", window);
最新版のコードは、GitHubの以下のサイトで公開されています。 https://github.com/jfriend00/docReady
使用方法
// pass a function reference
docReady(fn);
// use an anonymous function
docReady(function() {
// code here
});
// pass a function reference and a context
// the context will be passed to the function as the first argument
docReady(fn, context);
// use an anonymous function with a context
docReady(function(context) {
// code here that can use the context argument that was passed to docReady
}, ctx);
でテストされています。
IE6 and up
Firefox 3.6 and up
Chrome 14 and up
Safari 5.1 and up
Opera 11.6 and up
Multiple iOS devices
Multiple Android devices
動作する実装とテストベッド。 http://jsfiddle.net/jfriend00/YfD3C/
その仕組みについてまとめてみました。
- を作成します。 IIFE (即座に呼び出される関数式) を使用して、非公開の状態変数を持つことができるようにします。
-
パブリック関数を宣言する
docReady(fn, context)
-
いつ
docReady(fn, context)
が呼び出されたら、ready ハンドラがすでに起動されているかどうかをチェックします。 もしそうなら、新しく追加したコールバックを、このスレッドの JS が終了した直後に起動するようにsetTimeout(fn, 1)
. - ready ハンドラがまだ起動されていない場合は、この新しいコールバックを、後で呼び出されるコールバックのリストに追加します。
- ドキュメントがすでに準備されているかどうかを確認します。 もしそうなら、すべてのレディーハンドラを実行します。
- ドキュメントが準備完了になったことを知るためのイベントリスナーをまだインストールしていないのであれば、今すぐインストールしましょう。
-
もし
document.addEventListener
が存在する場合、イベントハンドラを.addEventListener()
の両方に対して"DOMContentLoaded"
と"load"
イベントを使用します。 load"は安全のためのバックアップイベントであり、必要ないはずです。 -
もし
document.addEventListener
が存在しない場合、イベントハンドラのインストールは.attachEvent()
に対して"onreadystatechange"
と"onload"
のイベントがあります。 -
での
onreadystatechange
イベントにおいてdocument.readyState === "complete"
で、もしそうなら、すべてのレディーハンドラを起動する関数を呼び出します。 - 他のすべてのイベントハンドラで、すべてのレディーハンドラを起動する関数を呼び出します。
- すべてのレディーハンドラを呼び出す関数の中で、ステート変数をチェックし、すでに発射したかどうかを確認します。 すでに発射されている場合は、何もしません。 まだ呼び出されていない場合は、ready 関数の配列をループして、追加された順番に各関数を呼び出します。 これらの関数がすべて呼び出されたことを示すフラグを設定し、2回以上実行されないようにします。
- 関数の配列をクリアし、使用しているクロージャを解放します。
で登録したハンドラ
docReady()
は、登録された順番に実行されることが保証されています。
を呼び出すと
docReady(fn)
を使用して現在の実行スレッドが完了すると同時にコールバックが実行されるようにスケジューリングされます。
setTimeout(fn, 1)
. これにより、呼び出し側のコードでは、たとえ「後で」が現在の JS のスレッドが終了した時点であっても、常に「後で」呼び出される非同期コールバックであると見なされ、呼び出し順序が維持されます。
関連
-
Vue Element-uiは、アイコンを追加するためのツリーコントロールノードを詳細に実装しています。
-
[解決済み】ローカルファイルを開くことができません - Chrome: ローカルリソースの読み込みが許可されていない
-
[解決済み】リソースの読み込みに失敗した:Bind関数でサーバーが500(Internal Server Error)のステータスで応答した【非公開
-
[解決済み】ExpressJS - throw er Unhandled errorイベント
-
[解決済み] JavaScriptのオブジェクトが空であることをテストするにはどうすればよいですか?
-
[解決済み] JavaScriptで空文字列/未定義文字列/null文字列をチェックするにはどうすればよいですか?
-
[解決済み] ページを再読み込みせずにURLを変更するにはどうすればよいですか?
-
[解決済み] JavaScriptでNULL、未定義、空白の変数をチェックする標準的な関数はありますか?
-
[解決済み] JavaScriptで現在の日付を取得するには?
-
[解決済み】JavaScriptの関数にデフォルトのパラメータ値を設定する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
WeChatアプレット用ユニアプリによるグローバルシェアリング
-
vue ディレクティブ v-html と v-text
-
vueにおけるv-forループオブジェクトのプロパティ
-
vueにおけるfilterの適用シーンについて解説します。
-
[解決済み】ローカルファイルを開くことができません - Chrome: ローカルリソースの読み込みが許可されていない
-
[解決済み】JavaScriptの配列でforEachが関数でない不具合
-
[解決済み】 env: node: macにそのようなファイルやディレクトリはありません
-
[解決済み】エラー。Ionic使用中にモジュール '../lib/utils/unsupported.js' が見つかりませんでした。
-
HTML5 LocalStorage ローカルストレージとセッションストレージの使用法
-
[解決済み] jQueryを使用しない$(document).ready相当