Drupal で JavaScript を使いこなす (FAQ を含む)
公開: 2023-02-07インタラクティブな Web エクスペリエンスは、より魅力的で楽しいエクスペリエンスをユーザーに提供します。 これにより、ユーザーの満足度が向上し、Web サイトに対する肯定的な認識が得られます。 たとえば、ユーザーがページの更新を待たされるのではなく、即座にフィードバックと検証を提供するフォームは、ユーザー エクスペリエンスを大幅に向上させることができます。
JavaScript は、Drupal Web サイトのフロントエンドでユーザーに動的でインタラクティブなエクスペリエンスを作成する手段を提供することで、Drupal で重要な役割を果たします。 これにより、開発者はページ全体を更新することなく、フォーム、リンク、またはその他の DOM 要素など、ページ上の特定の要素の動作を変更できます。 Drupal ビヘイビアは、ページで特定のイベントが発生したときに実行される JavaScript 関数です。 ビヘイビアーを使用すると、開発者は基盤となる HTML コードを変更する必要がないため、サイトの保守とアップグレードが容易になります。 この記事で、Drupal Behaviors について知りたいことをすべて見つけてください。
Drupal ビヘイビアとは
Drupal.behaviorsは、Drupal の Javascript 構造内のオブジェクトであり、アプリケーションの実行中に特定の時間に実行される関数をアタッチできます。 DOM が完全にロードされたときに呼び出されますが、これらの動作は再度呼び出すことができます。 Drupal の公式 JavaScript ドキュメントでは、モジュールはDrupal.behaviorsにロジックを追加して JavaScript を実装する必要があると提案しています。
Drupal の動作が必要な理由
ビヘイビアーの利点は、AJAX を介して読み込まれるすべてのコンテンツに自動的に再適用されることです。 これらは、DOM への新しい追加または変更を表すコンテキストでいつでも呼び出すことができます。 これは、コードが一度だけ実行される$(document).ready()またはdocument DOMContentLoadedよりも優れています。
Drupal の動作が望ましくないのはいつですか?
Drupal の動作は、Drupal で Javascript を作成するための完璧なソリューションとは限りません。 場合によっては、以下で説明するように、Drupal の動作がまったく必要ないこともあります。
- DOM に影響を与えないコードを実行する必要がある場合。 例えば。 Google アナリティクスなどの外部スクリプトの初期化
- ページがロードされたときに要素が使用可能になることがわかっている場合に、DOM で JS 操作を 1 回だけ実行する必要がある場合 (このシナリオは、1 回の使用とは異なります)。
Drupal ビヘイビアはいつ呼び出されますか?
- 管理オーバーレイがページにロードされた後。
- AJAX Form API がフォームを送信した後。
- AJAX リクエストがajax_command_replace()などの HTML を変更するコマンドを返す場合。
Drupal Behaviors が呼び出されるその他の場合
- CTools は、モーダルが読み込まれた後に呼び出します。
- メディア ブラウザがロードされた後、Media はそれを呼び出します。
- Panels は、インプレース編集が完了した後に呼び出します。
- Views は、AJAX を使用する新しいページをロードした後に呼び出します。
- Views Load More は、アイテムの次のチャンクをロードした後に呼び出します。
- カスタム モジュールの JavaScript は、ページの一部を追加または変更するときにDrupal.attachBehaviors()を呼び出す場合があります。
Drupal ビヘイビアなしでコードを書く
このコードでは、この行をクリックした回数を計算する.views-rowクラスにクリックイベント リスナーを追加しています。 ただし、最初のページの読み込み時に DOM に含まれる要素に 1 回だけ追加されます。 [さらに読み込む] をクリックしてさらに項目を読み込むと、新しく読み込まれた項目でクリック リスナーが機能しません。
// No Drupal Behaviors (function () { let header = document.querySelector(".food-list-header"); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = document.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); })();
Drupal ビヘイビアをどのように使用しますか?
回答: attachメソッドを使用する
覚えておくべきこと:
- 新しいオブジェクトには、少なくとも attach メソッドが必要です。
- Drupal.attachBehaviors が呼び出されるたびに、すべての動作オブジェクトを反復処理し、それぞれの attach メソッドを呼び出します。
Drupal の動作をコードに追加する
Drupal Behaviors を追加すると、コードは次のようになります。
(function (Drupal) { Drupal.behaviors.exampleBehaviour1 = { attach: (context, settings) => { // Add a delicious text to the top of the document let header = document.querySelector(".food-list-header"); // jQuery Equivalent // $(".food-list-header"); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = document.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal);
しかし、[もっと読み込む] をクリックすると、上部に奇妙なものが表示されます。
これは、Drupal の動作が何度も呼び出され、その後、意図しない動作が発生するためです。
「Drupal コンテキスト」におけるコンテキストとは?
- Drupal は、すべての動作に対して attach メソッドを呼び出すときに、コンテキストパラメーターを渡します。
- 多くの場合、渡されるコンテキスト パラメータによって、どの DOM 要素が処理されているかをよりよく把握できます。
- 最初のページの読み込み中、これは完全な HTML ドキュメントになります。 後続の呼び出しでは、これはページに追加される要素または変更される要素だけになります。
コンテキストを追加するには?
前の問題は、Drupal Behaviors が提供するコンテキストパラメーターを使用することで解決できます。 この場合、ページが初めて読み込まれるときに、HTML ドキュメント全体をコンテキストとして取得し、そのときにヘッダーをアタッチします。 以降の操作では、Drupal 動作の影響を受けるのはコードの一部であるため、コードのその部分は安全に制御されます。
(function (Drupal) { Drupal.behaviors.exampleBehaviour2 = { attach: (context, settings) => { // Add a delicious text to the top of the document. // The context parameter now can be used for adding // certain functionality which removes unwanted repeatability let header = context.querySelector(".food-list-header"); // jQuery Equivalent // $(".food-list-header", context); if (header) { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } // Add the event listener for each click on the food let foods = context.querySelectorAll(".views-row"); foods.forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal);
ここでも、 Load Moreをクリックすると奇妙な動作が発生します。 最初にロードされた食品は正常に機能します。 ただし、[さらに読み込む] をクリックすると、新しいアイテムはクリック リスナーを取得し、正常に動作します。 しかし、最初にロードされたアイテムにはリスナーが再度アタッチされ、それらをクリックするとクリック イベントが複数回呼び出されます。
Drupal Behaviors はいつ誤動作を開始しますか?
- Once と Context を使用せずに、Drupal ビヘイビア内のすべてのイベント リスナーを記述します。
- Drupal ビヘイビア内で不要な関数を宣言すると、attach メソッドが呼び出されるたびに関数が再宣言されます。
救助への「一度」
- Once は、コードの実行後に DOM 要素に data-once 属性を追加することで、何かが一度だけ処理されるようにします。
- 動作が再度呼び出されると、data-once 属性を持つ要素はスキップされて以降の実行が行われます。
- Once はjQuery.onceの最新の実装です (これは jQuery から離れようとする試みです)。
- 一度、コンテキストと組み合わせて、必要に応じて機能全体を完全に制御します。
Once を追加して、コード内のイベント リスナーを修正します
(function (Drupal, once) { Drupal.behaviors.exampleBehaviour3 = { attach: (context, settings) => { once("food-header-initialized", ".food-list-header", context).forEach( (header) => { let greatFoodSpan = document.createElement("span"); greatFoodSpan.textContent = "Get ready for great food!!!!!!"; header.append(greatFoodSpan); } ); // jQuery Equivalent // $(".food-list-header", context).once("food-header-initialized", function (header) { // // }); // Add the event listener for each click on the food once("food-initialized", ".views-row", context).forEach((food) => { food.addEventListener("click", () => { let foodCounter = food.querySelector(".food-click-counter"); let timesClicked = parseInt(foodCounter.textContent.trim()); foodCounter.textContent = ++timesClicked; }); }); }, }; })(Drupal, once);
これで、すべてが意図したとおりに機能します。 イベントリスナーがアタッチされ、新しくロードされた要素と以前にロードされた要素が適切に機能する要素にdata-once属性を取得します。
デタッチ方法の必要性
Detach メソッドはアンチ ヒーロー (悪ではない) のように機能し、attach メソッドで行ったことをすべて削除します。 コンテンツが DOM から削除されるたびに、detach メソッドのすべてのコードが呼び出されます。 これは、アプリケーションをクリーンアップするのに役立ちます。 たとえば、Detach メソッドを使用すると、継続的なポーリング状況などのリソースを消費する不要なイベント リスナーを削除できます。
デタッチの例
記入する ajax フォームがあり、タイマーを使用して経過時間を表示しているとします。 setTimeOut を使用してタイマーを管理します。 このタイマーは、監視のためにコンソールに記録されます。
(function (Drupal, once) { let counter = 0; Drupal.behaviors.exampleBehaviour4 = { attach: (context, settings) => { once("timer-initalized", ".contact-timer", context).forEach((ele) => { const timer = context.querySelector(".contact-timer-sec"); timer.textContent = counter; // Set the timer for user to see the time elapsed setInterval(() => { console.log("This is logging"); const timer = document.querySelector(".contact-timer-sec"); timer.textContent = ++counter; }, 1000); }); }, }; })(Drupal, once);
フォームの送信時に、DOM のタイマーは削除されますが、コンソールはエラーをスローし始めます。 これは、setTimeOut が作用する要素が DOM から削除されたためです。
これを回避するには、次のように detach メソッドを使用できます。
(function (Drupal, once) { let counter = 0; let intervalStopper; Drupal.behaviors.exampleBehaviour4 = { attach: (context, settings) => { // Set the timer for user to see the time elapsed once("timer-initialized", ".contact-timer", context).forEach((ele) => { const timer = context.querySelector(".contact-timer-sec"); timer.textContent = counter; intervalStopper = setInterval(() => { const timer = document.querySelector(".contact-timer-sec"); timer.textContent = ++counter; console.log("This is logging"); }, 1000); }); }, // Clear the timer on confirmation detach: (context, settings, trigger) => { const timer = context.querySelector(".contact-timer-sec"); if (trigger == "unload" && timer) { clearInterval(intervalStopper); } }, }; })(Drupal, once);
これにより、アンロード時のタイマーが削除され、ロガーからわかるように、エラーは発生しません。
即時呼び出し関数式 (IIFE) - JS のラッパー
Drupal コードを記述するために IIFE を使用しています。 最初の開き括弧は、関数のスコープがアプリケーション全体のグローバル スコープを汚染するのを防ぐのに役立つ無名関数を定義します。 関数定義の最後に引数として含めることで、無名関数に引数を渡すことができます。
これは、パラメーターの名前空間を変更するのにも役立ちます。
例:
// Function name crisis!!!! // The function is vulnearble to // be replaced by some other function function someFunction() { // Some code for this function } (function (Drupal) { // Function name crisis averted! function someFunction() { // Some code for this other function } Drupal.behaviors.exampleBehaviour6 = { attach: (context, settings) => { someFunction(); }, }; })(Drupal);
最終的な考え
Drupal ビヘイビアーを実装すると、動的なインタラクティブ性、合理化されたユーザー インタラクション、改善されたユーザー フィードバック、効率的な開発、および Web サイトの全体的なユーザー エクスペリエンスの向上が可能になります。 Drupal.behaviors は柔軟でモジュール化されているため、1 つのページで複数回実行でき、既存の動作をオーバーライドおよび拡張でき、Ajax を介して読み込まれたコンテンツに自動的に再適用できます。
Drupal を最大限に活用して、インタラクティブな Web エクスペリエンスの構築を支援する Drupal 開発エージェンシーをお探しですか? ぜひお話したいです!