Ajax遷移の際にContact Form 7のイベントを再登録する
直近の話でWordPressでちょっとしたサイトを組み上げているとき、サイト全体のページ遷移をAjaxで行わせることがあった。
そのとき、フォームの設置には毎度おなじみのContact Form 7を使ったのだが、メインコンテンツ部分のノードを書き換えた際に、ショートコードで設置したフォームが正常に動作しない(同期モードで実行される)という事象に遭遇したのでそこで少しハマった。
要はAjaxによる非同期通信でコンテンツのノードを書き換えた際に、読み込み後のフォームにContact Form 7のJavaScriptのイベントが登録されていないって話ではあるが、今回はそいつを再登録するようにしたかったので投稿。
まず大前提として、そもそもWordPressで制作したサイト全体をAjax化させる行為というのが試験的行為である。
WordPressのサイトのパラメータの形は多岐にわたって設定できるため、このパラメータを制作最初期の段階で定めておく必要があるからだ。
まぁ、どういう形態にするとしてもAjax化する方法は自前で作るとかプラグイン使うとか諸々あり、ここを説明してると七面倒臭いので今回は話の焦点からは外すが、まずこの試験的行為を行うためには普段意識せずに使い方に書かれたとおりにコピペで突っ込んでいるスクリプトに対する理解を行わなければならない。
これは件のContact Form 7のスクリプトに限った話ではなく、その他のJavaScriptに関しても同様である。
通常のページ遷移内でスクリプトを記載するところ、bodyの閉じタグ直前又はheadの中に記載することになるが、Ajaxでの遷移を行う場合はこれを切り分ける必要がある。
これ自体は以下のように大別される。
- 検索エンジン等々でダイレクトアクセスされた際の初回のページで初期化させるスクリプト(ヘッダ・フッタ等の共有部分)
- Ajaxであるノードに対してページデータを読み込んだ際に、ページの構造毎により逐一登録させるスクリプト(個別ページのスクリプト)
1.については、だいたいがヘッダのメニュー展開であったり、ページ上部に戻る際のボタンであったりとかが該当するだろう。無論、Ajaxのサイト全体遷移のスクリプトもここに位置する。
これらはページが初回読み込みされた際に実行するように、通常通りの記載となる。
2.については、あるサブページにしか使われていないパララックス・スクロールアニメーションだとか、トップページのスライドショー、QAページとかのトグルボタンとかなんかがおおよそ該当。
これらはAjax処理で現行ページから遷移先ページへノードが書き換えられた際に、余計なイベントを開放させるために実行するような記載となる。Ajaxのスクリプト実行時のいわゆるコールバックである。
それぞれ「初回ページ読み込み時」と「Ajaxでのページデータ読み込み毎時」とかでも言うもの。
ここで冒頭の話に戻ると、Contact Form 7のスクリプトはどちらに該当するかといえば、大雑把にどちらにも該当することができる。
なぜならば、functions.phpの設定にもよるが、デフォルトでContact Form 7のスクリプトというものは基本的には全てのページで読み込まれるからである。
「ちょっと待って!全てのページで読み込まれているなら、仮にAjaxでページ全体を遷移したとしても、Contact Form 7のよくわからないけど便利なコードが勝手に補正してくれるんじゃないの!?」
とか、思ったそこのあなた。
そいつはとんだ大間違いだ。
じゃなければこんな腐れコーダーの落書きみたいなブログを見ていることはないだろう。(ニヤリ)
そう、あくまでContact Form 7のスクリプトはページの毎度の読み込み時(=初回読み込み時)にドキュメントルート上から辿られた単一又は複数のフォームのノードに対して一度だけ実行されるものだ。
ユーザーが勝手に組み込んだor作ったスクリプトやプラグインに対してはな~んの補正もかけてくれないし、Contact Form 7の開発者さんはそのようなことをする責任も義務もない。
だって善意で作ってくださっている無料のプラグインなのだから。むしろ、それに大いに感謝するべきである。
そして、そのような素晴らしいプラグインに対して俺が陳腐な猿知恵を絞ってなにかできるわけではなく、そんな義理もありません!!!><
以上、おしまい。
・・・・。
・・・・・・・・。
・・・なんて、流石にそれでは投げやりに終わるので解決を図ってみる。
1.と2.で触れたとおり、「初回ページ読み込み時」と「Ajaxでのデータ読み込み毎時」で実行するスクリプトは切り分けておく必要がある。
Ajax遷移自体をWPのプラグインなどで実装している場合は、多分そのプラグイン設定のどこかに「ページの初回読み込み時」のスクリプトを設定する箇所とか「Ajaxでのページデータ読み込み毎時」のコールバックのスクリプトを設定する箇所があるんじゃないかと思う、知らんけど(ハナホジ)
まぁ、仮想対象のプラグインも思い浮かばないため、ここでは例として以下のような形とする。
// Ajaxでのサイト全体の遷移コード $.fn.ajaxSiteLoad = function( options ) { var option = { callbackFunction: null, }; var config = $.extend( option, options ); // ※※※Ajaxでページデータを読んでノードを書き換える処理※※※ config.callbackFunction(); // 関数をコールバック }; // 「Ajaxでの読み込み毎時」のコールバック用のコード var callbackScripts = function() { // ここにページデータ読みのノード書き換えの際に登録するイベントを記載 }; // 「初回ページ読み込み時」に最初に実行するコード var startScripts = function() { $('#hoge').ajaxSiteLoad({ // Ajaxでのサイト全体の遷移コードの呼び出し callbackFunction: callbackScripts, // コールバック用のコードを受け渡す }); // 他、ここで初回に登録したい諸々のイベントを記載 }; startScripts();
上記の例なら、callbackFunctionを引数としてajaxSiteLoad 側に渡してノードを書き換えた際に、コールバックとして実行させる。
なぜこの書き方をしているかは後記する。
で、次は肝心のContact Form 7を再度実行させるコード。
すなわちContact Form 7に非同期通信で送信処理をさせるためのイベントの再登録。
普通にContact Form 7のindex.jsを見ても、コンプレスされたJavaScriptコードがあるだけである。
これ以外にContact Form 7がイベントの登録のために実行しているスクリプトは見当たらない。
というか、コンプレスされたコードを顔をしかめて読んでみると、なんとイベントの登録しているだろう箇所までもがコンプレスされている。
流石にコンプレスされていると見づらいことのこの上ないので、これは【contact-form-7/includes/js/src】のあたりからコンプレスされる前のコードを引っ張ってくる。
どのメソッドの呼び出しでフォームにイベントを登録しているのかを探し出してトライアンドエラーするのにほんの少し手間がかかったが、バージョン5.4.1時点のソース版「index.js」の34~41行目あたりにイカニモな記述があった。
// 「contact-form-7/includes/js/src/index,js」より引用 const forms = document.querySelectorAll( '.wpcf7 > form' ); if ( typeof forms.forEach !== 'function' ) { console.error( "Your browser doesn't support NodeList.forEach()." ); return; } wpcf7 = { init, submit, reset, ...( wpcf7 ?? {} ), }; forms.forEach( form => wpcf7.init( form ) );
記述を読むあたり、定数の「forms」がドキュメントルート上のフォームノードを格納しているものだろう。
重要なのはそこではなく、注目するべきは「wpcf7.init( form )」の部分。イカニモなコードである。
面倒くさくて全文のコードを繋げて読んだわけでもないので「form」の部分については実行後からの推察だが、イベントトリガーかなんか?だと思う。(間違っていたらゴメンね!)
つまりこれは、自分のようなコーダーがパッと見で分かるように簡潔に書くなら以下のように書くこともできる。
document.querySelectorAll( '.wpcf7 > form' ).forEach( function( form ) { wpcf7.init( form ); });
で、試しにこれをContact Form 7のショートコードを入れたところに同時に書いてみたところ・・・。
・・・・。
・・・・・・・・。
動かねぇえええええええええ!!!!!1111
いや、そりゃそーだ。
だって、どのような読み込み方をしてもAjaxで読み込んできたテンプレート自体にはwpcf7なんてクラスはおろか、Contact Form 7の母体のスクリプト自体が存在しないわけだからね。
このイベントを再登録するコードは母体のスクリプトが読み込まれているドキュメント上に対してノードが書き換えられたタイミングで実行しなければならない。
同じ理論で「じゃあテンプレートに母体のスクリプトをそのまま組み込めばいいじゃね?」とか思うかもしれないけど、間違ってもそんな恥ずかしいことはダメ、ゼッタイ。(そのような荒業でも動くあたり本当によく出来てるけど・・・)
ともあれ、例として記載したコードのところでわざわざ下線を入れて「なぜこの書き方をしているかは後記する。」と書いたのかはこのためである。
なのできちんと、このように書いてあげる。
// 「Ajaxでの読み込み毎時」のコールバック用のコード var callbackScripts = function() { // Contact Form 7のフォームのイベントを再登録 if ( typeof wpcf7.init === 'function' ) { document.querySelectorAll( '.wpcf7 > form' ).forEach( function( form ) { wpcf7.init( form ); }); } // ここにページデータ読みのノード書き換えの際に登録するイベントを記載 };
結果は・・・・、
・・・・・・・・。
やたー!解決したよー!ぱちぱち
念の為、おまじないとしてwpcf7.initの存在も確認しておく。
コンパチする場合、関数とかクラスとかメソッドの存在確認は結構大事やし。(´・ω・`)
上記のコードはAjaxでの全ページ遷移だけに限らず、原因はよーわからんけどとりあえず非同期でContact Form 7が動作しないといった場合にも使えるように思う。
(まぁ、普通に組み込むだけならそんなことは滅多に起きないとは思うけど・・・)
以上、今度こそ本当におしまい。
・・・サイトの公開が終わったら、作ったAjax遷移のコードを暇を見つけて載せるかねぇ。
過去に書いたものと完全に別物やしなー。