web-dev-qa-db-ja.com

ajaxされたコンテンツのすべて(スクリプト/関数)の強制的な再起動

tl; dr

私はajaxを使用して新しいコンテンツを取得します。コンテンツが取得され、ページに追加されます。ただし、スクリプトはajaxed divの外にあるため、スクリプトは「再起動」しません。

スクリプトは、最初のページの読み込みで問題なく読み込まれて起動しますが、ajaxを介して新しいコンテンツを追加するときは起動しません。コンソールエラーは発生せず、URLに直接アクセスしても問題はありません。


関連:

  1. スクリプトを強制的に実行AJAXロードされたページ -1つの特定のスクリプトに関連しています。動的スクリプトを含むすべてのスクリプトを修正(再起動)したい Cloudflareアプリ
  2. WordPressのAjaxでjQueryスクリプトを使用 -上記と同じ、これは1つの特定のスクリプトにのみ関連します
  3. ajaxロードされたコンテンツ、スクリプトは実行されていません

イントロ:

WordPressウェブサイトで Ajaxify WordPress Site(AWS) を使用しています。

プラグインを使用すると、IDによってdivを選択し、ajaxを使用してそのdiv内の新しいコンテンツをフェッチできます。

hTMLマークアップ

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div> 
    </main> <!-- end of ajaxfied content  -->
    <footer></footer>
  </div> <!-- #page -->
</body>

</html>

問題

プラグインは機能し、新鮮なコンテンツをロードしてスタイルを設定しましたが、問題があります。ページの一部スクリプトと関数呼び出しは、ajaxを使用しているdivの外にあります。 2つの例があります

1-石積みが読み込まれ、<footer>で呼び出されます

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>   <!-- end of ajaxfied content  -->
    <footer></footer> <!-- Masonry script is loaded and called here -->
  </div> <!-- #page -->
</body>

</html>

2- Googleマップの通話は<head>にあります

<html>

<head></head>  <!-- Google maps is called here -->

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>  <!-- end of ajaxfied content  -->
    <footer></footer> 
  </div> <!-- #page -->
</body>

</html>

これらは2つだけです。他の場所には他にもあります。ご存知のように、ページでリロードするのは<main id="ajax">だけなので、そのようなスクリプトは再度呼び出されません。 <main>内の新しいコンテンツは存在しますですが、適切にレンダリングするために必要なスクリプトの一部は再呼び出しされないため、要素が見つからないか、レイアウトが壊れています。


この問題に直面したのは私だけではありません。プラグインの wordpress.orgのサポートフォーラム をざっと見ると、この問題が一般的であることがわかります。

注:プラグインに他の多くの問題がある場合、私はこれを修正しようとはしません。私にとってはうまくいきます。スクリプトを再起動する必要があります。

公式の回答は、プラグインのphpファイルに以下を追加することでスクリプトをリロード/再起動することが可能であるということです:

$.getScript(rootUrl + 'PATH TO SCRIPT');

うまくいきました。それはうまくいきます。たとえば私が追加した場合

$.getScript(rootUrl + '/Assets/masonry.js');

次に、masonry.jsがajaxされたdivの外部にロードされている場合でも、ajaxされたコンテンツがフェッチされると、masonry関数呼び出しが再起動されます。

修正の実際の動作をより明確にするために github上のプラグインのファイル を参照します($.getScriptを使用すると何が起こるか理解できません)


要約すれば

Ajaxされたコンテンツで再起動する必要がある3〜4つのスクリプトがある場合、公式の修正は正常に機能します。

これは私の目標にはうまくいきません

  1. 厳格すぎてハードコードされている
  2. 一部のスクリプトは Cloudflare apps を介して動的にページに追加されます

可能な解決策としては、イベントを追加して、ajaxed divの下部でスクリプトを起動させるトリガーを模倣することが考えられます。

質問:

すべてのスクリプトを強制するにはどうすればよいですか動的に追加されたスクリプトを含む-ページの特定の部分のみがajax経由でリロードされたときに再起動しますか?


注:

  1. 事前にスクリプトの呼び出しに関する知識が必要になるため、スクリプトを1つずつ呼び出さないようにしています。私はおそらく私の頭の上で話しているですが...

    mimicページの読み込みおよび/またはドキュメント準備イベント-most条件付きスクリプトが起動されます(間違っている場合は修正してください)-新しいajaxされたコンテンツが追加されたときにhtmlの<main>の最後に、ドキュメントに影響を与えずにページは、URLを直接使用してロードされます...または他の同様のアプローチ。

  2. ちょっとしたコンテキストについてのみ]これは、ページ上のいくつかのイベントリスナーのリストですプラグインがオフのとき。トリガーする必要がないものがそこにあることは知っています。参考のために追加しました。また、これはいずれかのページからのサンプルであることに注意してください。他のページは異なる場合があります。

DOMContentLoaded
beforeunload
blur
click
focus
focusin
focusout
hashchange
keydown
keyup
load
message
mousedown
mousemove
mouseout
mouseover
mouseup
mousewheel
orientationchange
readystatechange
resize
scroll
submit
touchscreen
unload
35
I haz kode

ここで選択するソリューションは、スクリプトの初期化方法に依存する必要があります。いくつかの一般的な可能性があります。

  1. スクリプトのロード時にスクリプトのアクションが直ちに呼び出されます。この場合、スクリプトは次のようになります。

    _(function() {
         console.log('Running script...');
    })();
    _
  2. スクリプトのアクションは、いくつかのイベントに応答して呼び出されます(ドキュメント準備完了(JQuery)またはウィンドウオンロード(JavaScript)イベントなど)。この場合、スクリプトは次のようになります。

    _$(window).on('load', function() {
        console.log('Running script...');
    });
    _

これらの2つの可能性のいくつかのオプションを以下で検討します。

ロード時にすぐに実行されるスクリプトの場合

1つのオプションは、トリガーする_<script>_要素をDOMから削除してから再接続することです。 JQueryを使用すると、次のことができます

_$('script').remove().appendTo('html');
_

もちろん、上記のスニペット自体が_<script>_タグ内にある場合、すべてのスクリプトを常に切り離して再接続する無限ループを作成します。さらに、再実行したくないスクリプトがあるかもしれません。この場合、スクリプトにクラスを追加して、肯定的または否定的にクラスを選択できます。例えば、

_// Positively select scripts with class 'reload-on-ajax'
$('script.reload-on-ajax').remove().appendTo('html');

// Negatively select scripts with class 'no-reload'
$('script').not('no-reload').remove().appendTo('html')
_

あなたの場合、上記のスニペットの1つをAJAX完了のイベントハンドラーに配置します。次の例では、AJAX完了イベントの代わりにボタンクリックを使用していますが、概念は同じです(この例はStackOverflowサンドボックス内ではうまく機能しないため、別のページとしてロードしてみてください最高の結果):

_<html>
  <head></head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

  <script class="reload-on-ajax">
    console.log('Script after head run.');
  </script>

  <body>
    <button id="reload-scripts-button">Reload Scripts</button>
  </body>

  <script class="reload-on-ajax">
    console.log('Script after body run.');
  </script>

  <script>
     $('#reload-scripts-button').click( () => $('script.reload-on-ajax').remove().appendTo('html') );
  </script>
</html>_

スクリプトがインラインでない場合(たとえば、src属性を介して取得される場合)、ブラウザーと設定に応じて、サーバーから再ロードされるか、ブラウザーのキャッシュから取得されます。この場合、最善のアプローチは、AJAXでロードされたコンテンツを操作するすべての_<script>_ sを削除し、AJAX補完からJQueryのgetScript()関数などを介してロード/実行することです。イベントハンドラ。このようにして、適切なタイミングで(つまり、AJAXコンテンツがロードされた後に)スクリプトを1回だけロード/実行します。

_// In AJAX success event handler
$.getScript('script1.js');
$.getScript('script2.js');
_

このアプローチの両方のバリアントの潜在的な問題は、スクリプトの非同期読み込みがクロスオリジン制限の影響を受けることです。したがって、スクリプトが別のドメインでホストされていて、クロスオリジンリクエストが許可されていない場合、機能しません。

イベントに応答して実行されるスクリプトの場合

スクリプトがウィンドウの読み込み時にトリガーされる場合は、このイベントをトリガーするだけです。

_   $(window).trigger('load');
_

残念ながら、スクリプト自体がJQueryのdocument readyイベントを使用している場合、手動でトリガーする簡単な方法はわかりません。他のイベントに応答してスクリプトが実行される可能性もあります。

もちろん、必要に応じて上記のアプローチを組み合わせることができます。そして、他の人が述べたように、スクリプト内に呼び出すことができる初期化関数がある場合は、それが最もクリーンな方法です。

15
cjg

外部スクリプトでグローバル初期化関数またはコードブロックを特定できる場合は、「ajaxComplete」イベントを確認できます。このコードをページヘッドに配置し、初期化関数呼び出しまたはコードブロックをajaxCompleteコールバック内に配置できます。

$(document).ajaxComplete(function(){
    module1.init();
    $('#my_id').module2_init({
        foo : 'bar',
        baz : 123
    });
});

あなたが話しているスクリプトがそのような使いやすい公開された初期化関数を持っていないが、scriptloadで自分自身を初期化する場合、すべてのスクリプトで機能するすぐに使えるメソッドはないと思います。

5
Benni

ここであなたが試すことができるものです-

石工やGoogleマップなどのスクリプトのほとんどは、ウィンドウのサイズ変更時に再初期化するように設定されています。したがって、ajaxの完了後にサイズ変更イベントをトリガーすると、これらのスクリプトを自動的に再起動するのに役立ちます。

次のコードを試してください-

$( document ).ajaxComplete( function() {
    $( window ).trigger('resize');
} );

これにより、コンテンツがロードされた後にサイズ変更イベントをトリガーするため、ajaxが完了するとスクリプトが強制的に再初期化されます。

5
Nitin Yawalkar

WordPressでajaxを使用してブラウザの状態とhistory.jsでページをリロードしようとすると、この問題が発生しました。プラグインを使用する代わりに、history.jsを直接エンキューしました。

新しいページがクリックされるたびに「再実行」する必要のある大量のJSがありました。これを行うために、メインのJavaScriptファイルにglobal_scripts();というグローバル関数を作成しました

まず、このJSファイルが他のすべての後にフッター.phpにエンキューされていることを確認します。

これは次のようになります。

wp_enqueue_script('ajax_js', 'url/to/file.js', 'google-maps', 1, true);

エンキューする私のJavaScriptは以下のとおりです。

jQuery(document).ready(function($) {

    // scripts that need to be called on every page load
    window.global_scripts = function(reload) {


       // Below are samples of the javascript I ran that I needed to re run.
       // This includes lazy image loading, paragraph truncation.

       /* I would put your masonry and google maps code in here. */

        bLazy = new Blazy({
            selector: '.featured-image:not(.no-blazy), .blazy', // all images
            offset: 100
        });


        /* truncate meeeee */
        $('.post-wrapper .post-info .dotdotdot').dotdotdot({
            watch: 'window'
        });

    }

    // call the method on initial page load (optional)
    //global_scripts();

});

次に、ページがhistory.jsでロードされ、global_scripts()が呼び出されるたびに実行されるJSにフックしました。

使用しているプラ​​グインもhistory.jsを使用しているようです。具体的にはプラグインでテストしていませんが、以下に投稿したもの(私がアプリで使用するもの)を試すことができます。これは上記と同じJSファイルに入れられます。

History.Adapter.bind(window, 'statechange', function() { 
     global_scripts();
}

私のコードは、上記に単に貼り付けたものよりも少し複雑です。実際にイベントをチェックして、ロードされているコンテンツを判別し、それに基づいて特定のJS関数をロードします。リロードされるものをより詳細に制御できます。

注:このコードを保持する個別のJSファイルがあるため、関数を宣言するときにwindow.global_scriptsを使用します。それらがすべて同じである場合、これを削除することを選択できます。

注2:この回答ではリロードする必要があるものを正確に把握する必要があることを理解しているため、最後のメモを無視します。このため、そのような知識は必要ありません。それでもあなたの答えを見つけるのに役立つかもしれません。

4
Jacob Raccuia

解決策は、すべてのスクリプトを複製することです...

$(document).ajaxSuccess((event, xhr, settings) => {
  // check if the request is your reload content
  if(settings.url !== "myReloadedContentCall") {
    return;
  }

  return window
    .setTimeout(rejs, 100)
  ;
});

function rejs() {
  const scripts = $('script[src]');
  // or, maybe alls but not child of #ajax
  // const scripts = $('*:not(#ajax) script[src]');

  Array
    .prototype
    .forEach
    .call(scripts, (script, index) => {
      const $script = $(script);

      const s = $('<script />', {
        src: $script.attr('src')
      });  
      // const s = $script.clone(); // should also work

      return s
        .insertAfter($script)
        .promise()
        .then(() => $script.remove()) // finally remove it
      ;
    })
  ;

}
4
Hitmands

危険かもしれませんが、<main>要素でDOMSubtreeModifiedを使用できるはずです。

$('#ajaxed').bind('DOMSubtreeModified', function(){
  //your reload code
});

次に、リロード領域にすべてのスクリプトを再度追加できるようにする必要があります

var scripts = document.getElementsByTagName('script');
for (var i=0;i<scripts.length;i++){
   var src = scripts[i].src;
   var sTag = document.createElement('script');
   sTag.type = 'text/javascript';
   sTag.src = src;
   $('head').append(sTag);

}

別のオプションとして、独自のイベントリスナーを作成し、同じリロードコードを含めることもできます。

$(document).on('ajaxContentLoaded', function(){//same reload code});

次に、コンテンツが更新されたら、プラグインでイベントをトリガーできます。

$(document).trigger('ajaxContentLoaded');

または、プラグインを編集してリスナーをトリガーすることと、コードベースに追加して、リロードするのではなく、リスナーを再実行する必要があると思われるものを再実行することの組み合わせ。

$(document).on('ajaxContentLoaded', function(){
   //Javascript object re-initialization
   myObj.init();
   //Just a portion of my Javascript?
   myObj.somePortion();
});
4
lscmaro

短い答え:これを一般的な方法で行うことはできません。スクリプトは、どのイベントを発生させる必要があるかをどのように知る必要がありますか?

より長い答え:プログラマティックな質問というよりは、構造的な質問のようなものです。必要な機能を担当するのは誰ですか?石積みを例にとりましょう:

Masonry.js自体はイベントをリッスンしません。石積みのインスタンスを自分で作成する必要があります(これは、おそらくWordpressプラグインでdomreadyで行われます)。サイズ変更オプションを有効にすると、リスナーがサイズ変更イベントに追加されます。しかし、実際に必要なのは、「DOMコンテンツの変更」のリスナーです(可能な解決策については https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver を参照してください)。 .jsはこのような機能を提供していません。次のオプションがあります。

  1. Masonry.jsでの実装を待つ(または自分で行う)
  2. 石積みの実装を待つ(または自分で行う)Wordpressプラグイン。
  3. 別の場所に機能を追加します(テンプレート、ajaxプラグインなど)4。

ご覧のとおり、すべてのオプションには実行すべき作業が含まれており、この作業はAJAX呼び出されたDOMの変更をリッスンするすべてのスクリプトに対して実行する必要があります。

3
Nils Rückmann