jQuery / メソッド / .load() - HTMLを要素内に読み込む

jQueryの.load(url, 完了関数)はHTMLを非同期取得し、選択要素の内部を置き換えます。.load("fragment.html #card", 完了関数)のように空白と選択子を付けると応答の一部だけ入れます。次の行で取得済みと仮定せず完了関数で処理します。

対象・戻り値・準備

.load()の仕様の対象は既存コンテナーです。外側は残り内側だけ変わります。空の選択なら要求しません。戻り値は元のjQueryオブジェクトであり、要求のjqXHRや完了HTML文字列ではありません。

Ajaxを除くslimでなく公式完全版を使います。Ajaxはページ全体を開き直さず通信する仕組みです。HTML取得の.load(url)は古い画像loadイベント短縮形と区別します。

新しい練習フォルダーに以下の二ファイルを作ります。ダブルクリックのfile://でなくHTTP経由です。同一オリジン規則はスキーム・ホスト・ポートを比較するため同じサーバーで提供します。localhostと127.0.0.1や異なるポートを混ぜません。

二ファイルの完全な例

まずUTF-8のfragment.htmlを作ります。カードと外側文段を分け、無害なスクリプトは実行回数だけ数えます。不明な外部コードに置き換えません。

<section id="card">
  <h2>Local fragment</h2>
  <p>This card came from fragment.html.</p>
  <button type="button" class="fragment-action">Count a delegated click</button>
</section>
<p id="outside">Outside the selected card</p>
<script>window.fragmentRuns = (window.fragmentRuns || 0) + 1;</script>

同じ場所にload-demo.htmlを保存します。#panelは要求前に存在します。重複操作や要求中の初期化を防ぐため完了まで操作部を無効にします。完了関数のthisは各受信対象です。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Load local HTML into a target</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 760px; margin: 24px auto; padding: 16px; }
    button { font: inherit; margin: 4px; max-width: 100%; }
    #panel { padding: 12px; border: 1px solid teal; min-height: 60px; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <h1>Load a complete response or selected fragment</h1>
  <button type="button" id="selected">Load #card only</button>
  <button type="button" id="whole">Load whole fragment</button>
  <button type="button" id="error">Request missing file</button>
  <button type="button" id="reset">Reset</button>
  <div id="panel">Initial content</div>
  <p id="clicks">delegated clicks=0</p>
  <pre id="status" aria-live="polite">Ready</pre>
  <script>
    $(function () {
      let busy = false, clicks = 0;
      window.fragmentRuns = 0;
      function load(url) {
        if (busy) return;
        busy = true;
        $("#selected, #whole, #error, #reset").prop("disabled", true);
        $("#status").text("Loading...");
        $("#panel").load(url, function (response, textStatus, xhr) {
          if (textStatus === "error") $(this).text("Load failed; try again.");
          $("#status").text(`status=${textStatus}; HTTP=${xhr.status}\nscript runs=${window.fragmentRuns}`);
          busy = false;
          $("#selected, #whole, #error, #reset").prop("disabled", false);
        });
      }
      $("#selected").on("click", function () { load("fragment.html #card"); });
      $("#whole").on("click", function () { load("fragment.html"); });
      $("#error").on("click", function () { load("missing-fragment.html"); });
      $("#panel").on("click.loadLesson", ".fragment-action", function () { clicks++; $("#clicks").text(`delegated clicks=${clicks}`); });
      $("#reset").on("click", function () {
        $("#panel").text("Initial content"); window.fragmentRuns = 0; clicks = 0;
        $("#clicks").text("delegated clicks=0"); $("#status").text("Ready");
      });
    });
  </script>
</body>
</html>

要求前の初期表示です。

Ready

Python 3を用意し、練習フォルダーでターミナルから次を実行します。pythonが見つからなければインストールと実行パスを確認します。8000使用中なら8001などへ変え、ブラウザーの番号も合わせます。

python -m http.server 8000 --bind 127.0.0.1

サーバー実行中にhttp://127.0.0.1:8000/load-demo.htmlを開きます。jQueryのコンテンツ配信ネットワーク(CDN)へはインターネットも必要です。終了時はターミナルでCtrl+Cです。3.7.1・4.0.0、Windows Edge、Python http.serverで実行し、検証ポートだけ空いている番号に変えました。

一部・全体・失敗を比較する

  1. Load #card onlyでLocal fragmentとカードだけ入り、status=success; HTTP=200、script runs=0です。外文段は入りません。
  2. カード内のCount a delegated clickでdelegated clicksが増えます。#panelに一度登録した委譲処理です。
  3. Load whole fragmentは外文段も入れscript runsを1増やします。後述のスクリプト差です。
  4. Request missing fileはstatus=error; HTTP=404です。エラー応答HTMLでなく固定文を.text()で示します。
  5. Load #card onlyで再試行できます。Resetは初期内容・数値に戻し、再取得でも委譲処理は重複登録しません。

"fragment.html #card"の空白後はブラウザーで応答を選ぶ選択子です。URLハッシュ"fragment.html#card"とは違います。#card自身と子が#panelに入ります。全HTMLを受けてから絞るため転送量の自動削減ではありません。一致がなければHTTP成功でも内部が空の場合があります。

スクリプト・イベントとGET・POST

公式仕様は選択子なしでHTML処理中にスクリプト実行の可能性があり、選択子付きでは挿入前に除くと説明します。二ボタンのカウンターで確認できます。これはすべての危険HTMLを無害化する保証ではありません。信頼するHTMLだけ入れ、初期化は完了関数や明示関数で管理します。

旧子に直接付けた処理は新子へ自動移行しません。例は残る#panelに委譲します。複数対象へ同じidのHTMLを繰り返すと重複するためテンプレートも確認します。完了関数は対象ごとに動くので全体処理の重複にも注意します。

dataがオブジェクトならPOST、それ以外はGETです。このPython静的サーバーはHTML GET練習用でPOST処理バックエンドではありません。オブジェクト追加前に受信側を確認します。HTML挿入とJSON解析も別作業です。

早すぎる読み取り・要求なしを確認する

開発者ツールNetworkで要求・状態・応答を見ます。load直後の#panelは旧内容の場合があるため挿入後の処理は完了内です。相対URLは現在ページ位置から解決するためフォルダーとサーバーも確認します。

要求なしなら.length、完全版、スクリプトエラーを確認します。異なるオリジンはサーバー側の適切な許可が必要な場合があり、ブラウザー保護を無効にして回避しません。中止・タイムアウト等には要求オブジェクトを返すjQuery.ajax()を使い、loadの戻り値に.abort()を期待しません。