在 Drupal 中馴服 JavaScript(包括常見問題解答)

已發表: 2023-02-07

交互式 Web 體驗為用戶提供了更具吸引力和愉悅的體驗。 它可以提高用戶滿意度和對網站的積極看法。 例如,提供即時反饋和驗證的表單,而不是讓用戶等待頁面刷新,可以顯著改善用戶體驗。

JavaScript 通過提供在 Drupal 網站前端為用戶創建動態和交互式體驗的方法,在 Drupal 中發揮著重要作用。 它使開發人員能夠修改頁面上某些元素的行為,例如表單、鏈接或任何其他 DOM 元素,而無需刷新整個頁面。 Drupal 行為是在頁面上發生特定事件時執行的 JavaScript 函數。 行為使開發人員可以輕鬆維護和升級站點,因為他們不需要更改任何底層 HTML 代碼。 在文章中找到您想了解的有關 Drupal 行為的所有信息。

在 Drupal 中馴服 JavaScript

什麼是 Drupal 行為?

Drupal.behaviors是 Drupal 中 Javascript 結構中的一個對象,它允許我們附加要在應用程序執行期間的特定時間執行的函數。 它在 DOM 完全加載時調用,但這些行為可以再次調用。 Drupal 的官方 JavaScript 文檔建議模塊應該通過將邏輯附加到Drupal.behaviors來實現 JavaScript。

為什麼我們需要 Drupal 行為?

行為的優點是它們會自動重新應用於通過 AJAX 加載的任何內容。 可以隨時使用表示對 DOM 的新添加或更改的上下文調用它們。 這比代碼只運行一次的$(document).ready()文檔 DOMContentLoaded更好。

什麼時候 Drupal 行為是不需要的?

Drupal 行為並不總是在 Drupal 中編寫 Javascript 的完美解決方案。 在某些情況下,如下所述,根本不需要 Drupal 行為!

  • 當我們需要執行一些不影響 DOM 的代碼時。 例如。 初始化外部腳本,如 Google Analytics
  • 當某些 JS 操作需要在 DOM 上執行一次時,知道該元素將在頁面加載時可用(這種情況與使用 Once 不同)。

什麼時候調用 Drupal 行為?

  • 在將管理疊加層加載到頁面中之後。
  • AJAX 表單 API 提交表單後。
  • 當 AJAX 請求返回修改 HTML 的命令時,例如ajax_command_replace()

其他時候調用 Drupal 行為

  • CTools 在加載模式後調用它。
  • Media 在加載媒體瀏覽器後調用它。
  • Panels 在就地編輯完成後調用它。
  • Views 在加載使用 AJAX 的新頁面後調用它。
  • Views Load More 在加載下一個項目塊後調用它。
  • 來自自定義模塊的 JavaScript 在添加或更改頁面的某些部分時可能會調用Drupal.attachBehaviors()

編寫沒有 Drupal 行為的代碼

在這段代碼中,我們向.views-row類添加了一個點擊事件監聽器,它計算我們點擊該行的次數。 但是它只在初始頁面加載期間添加到 DOM 中的元素一次。 單擊加載更多並加載更多項目後,點擊偵聽器不適用於新加載的項目。

 // 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方法

要記住的事情:

  • 新對象至少需要有一個附加方法。
  • 任何時候調用 Drupal.attachBehaviors 時,它都會遍歷所有行為對象並調用它們各自的附加方法。

將 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);

但是當我們點擊Load More 時,頂部會出現一些奇怪的東西:

行為

這是因為 Drupal 行為被調用了很多次,隨後我們得到了一些意想不到的行為。

“Drupal 上下文”中的上下文是什麼?

  • 當為所有行為調用附加方法時,Drupal 會傳遞一個上下文參數。
  • 傳遞的上下文參數通常可以更好地了解正在處理的 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時,再次出現一些奇怪的行為。 最初裝載的食品工作正常。 但是在點擊 Load More 之後,新項目獲得點擊監聽器並正常工作。 但是最初加載的項目會再次附加監聽器,點擊它們會不止一次調用點擊事件!

裝載更多

Drupal Behaviors 什麼時候開始行為不端?

  • 在不使用 Once 和 Context 的情況下在 Drupal 行為中編寫所有事件偵聽器。
  • 在 Drupal 行為中聲明不需要的函數,這會導致每次調用附加方法時都重新聲明函數。

“曾經”拯救

  • Once 通過在代碼執行後在 DOM 元素中添加 data-once 屬性來確保僅處理一次。
  • 如果再次調用該行為,則跳過具有 data-once 屬性的元素以繼續執行。
  • Once 是jQuery.once的現代實現(這是擺脫 jQuery 的一種努力)
  • 一次,結合上下文,完美地控制整個功能,如我們所需要的那樣。

添加一次以修復我們代碼中的事件偵聽器

(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);
數據一次

現在一切都按預期工作。 我們為附加了事件偵聽器的元素獲得了一次數據屬性,並且新加載的元素和先前加載的元素正常運行。

需要分離方法

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);

這會刪除卸載時的計時器,並且從記錄器中可以看出,錯誤不會發生。

JS 演示

立即調用函數表達式 (IIFE) - JS 的包裝器

我們一直在使用 IIFE 來編寫我們的 Drupal 代碼。 最初的左括號定義了一個匿名函數,它有助於防止函數的作用域污染整個應用程序的全局作用域。 您可以通過將參數作為參數包含在函數定義的末尾來將參數傳遞給匿名函數。

這也有助於我們為參數命名空間,但我們希望使用它們。

例子:

 // 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 行為允許動態交互、簡化用戶交互、改進用戶反饋、高效開發和整體增強網站的用戶體驗。 Drupal.behaviors 是靈活的和模塊化的,因為它們可以在頁面上多次執行,可以覆蓋和擴展現有行為,並且可以自動重新應用於通過 Ajax 加載的任何內容。

正在尋找 Drupal 開發機構來幫助您構建交互式 Web 體驗,充分利用 Drupal? 我們很想談談!