ทำให้ JavaScript เชื่องใน Drupal (รวมถึงคำถามที่พบบ่อย)

เผยแพร่แล้ว: 2023-02-07

ประสบการณ์เว็บแบบโต้ตอบมอบประสบการณ์ที่น่าดึงดูดและสนุกสนานสำหรับผู้ใช้ นำไปสู่ความพึงพอใจของผู้ใช้ที่เพิ่มขึ้นและการรับรู้เชิงบวกต่อเว็บไซต์ ตัวอย่างเช่น แบบฟอร์มที่ให้คำติชมทันทีและการตรวจสอบ แทนที่จะให้ผู้ใช้รอการรีเฟรชหน้า สามารถปรับปรุงประสบการณ์ผู้ใช้ได้อย่างมาก

JavaScript มีบทบาทสำคัญใน Drupal โดยมอบวิธีการสร้างประสบการณ์แบบไดนามิกและการโต้ตอบสำหรับผู้ใช้ที่ส่วนหน้าของเว็บไซต์ Drupal ช่วยให้นักพัฒนาแก้ไขลักษณะการทำงานขององค์ประกอบบางอย่างในหน้า เช่น แบบฟอร์ม ลิงก์ หรือองค์ประกอบ DOM อื่นๆ โดยไม่ต้องรีเฟรชทั้งหน้า Drupal Behaviors เป็นฟังก์ชัน JavaScript ที่ทำงานเมื่อมีเหตุการณ์บางอย่างเกิดขึ้นบนหน้าเว็บ ลักษณะการทำงานช่วยให้นักพัฒนาดูแลรักษาและอัปเกรดไซต์ได้ง่าย เนื่องจากไม่ต้องเปลี่ยนโค้ด HTML พื้นฐานใดๆ ค้นหาข้อมูลทั้งหมดที่คุณต้องการทราบเกี่ยวกับพฤติกรรมของ Drupal ในบทความ

ทำให้ JavaScript เชื่องใน Drupal

พฤติกรรมของ Drupal คืออะไร?

Drupal.behaviors เป็นวัตถุภายในโครงสร้าง Javascript ใน Drupal ซึ่งทำให้เราสามารถแนบฟังก์ชันที่จะดำเนินการในบางช่วงเวลาระหว่างการดำเนินการของแอปพลิเคชัน ซึ่งจะถูกเรียกเมื่อโหลด DOM เต็มแล้ว แต่ลักษณะการทำงานเหล่านี้สามารถเรียกได้อีกครั้ง เอกสาร JavaScript อย่างเป็นทางการของ Drupal แนะนำว่าโมดูลควรใช้ JavaScript โดยแนบตรรกะกับ Drupal.behaviors

ทำไมเราต้องมีพฤติกรรม Drupal?

ข้อดีของ Behaviors คือนำไปใช้ซ้ำโดยอัตโนมัติกับเนื้อหาใดๆ ที่โหลดผ่าน AJAX สามารถเรียกใช้ได้ทุกเมื่อด้วยบริบทที่แสดงถึงการเพิ่มใหม่หรือการเปลี่ยนแปลงใน DOM สิ่งนี้ดีกว่า $(document).ready() หรือ เอกสาร DOMContentLoaded ที่โค้ดถูกเรียกใช้เพียงครั้งเดียว

พฤติกรรม Drupal ไม่พึงประสงค์เมื่อใด

พฤติกรรมของ Drupal ไม่ใช่วิธีแก้ปัญหาที่สมบูรณ์แบบสำหรับการเขียน Javascript ใน Drupal เสมอไป ในบางกรณี ตามที่ระบุไว้ด้านล่าง พฤติกรรมของ Drupal ไม่จำเป็นเลย!

  • เมื่อเราต้องการเรียกใช้โค้ดบางอย่างที่ไม่ส่งผลกระทบต่อ DOM เช่น. การเริ่มต้นสคริปต์ภายนอก เช่น Google Analytics
  • เมื่อจำเป็นต้องดำเนินการ JS บางอย่างใน DOM เพียงครั้งเดียวเมื่อทราบว่าองค์ประกอบจะพร้อมใช้งานเมื่อโหลดหน้าเว็บ (สถานการณ์นี้แตกต่างจากการใช้ Once)

Drupal Behavior เรียกว่าเมื่อใด

  • หลังจากโหลดโอเวอร์เลย์การดูแลระบบเข้ามาในเพจแล้ว
  • หลังจาก AJAX Form API ส่งแบบฟอร์มแล้ว
  • เมื่อคำขอ AJAX ส่งคืนคำสั่งที่แก้ไข HTML เช่น ajax_command_replace()

ในบางครั้งเมื่อมีการเรียกใช้งาน Drupal Behaviors

  • CTools เรียกมันหลังจากโหลดโมดอลแล้ว
  • สื่อเรียกมันหลังจากโหลดเบราว์เซอร์สื่อแล้ว
  • แผงเรียกมันหลังจากการแก้ไขแบบแทนที่เสร็จสิ้น
  • 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 อย่างไร

คำตอบ: ใช้วิธีการ แนบ

สิ่งที่ต้องจำ:

  • วัตถุใหม่ต้องมีวิธีการแนบเป็นอย่างน้อย
  • เมื่อใดก็ตามที่เรียก 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);

แต่มีบางอย่างแปลก ๆ ปรากฏขึ้นที่ด้านบนเมื่อเราคลิก โหลดเพิ่มเติม:

พฤติกรรม

นี่เป็นเพราะพฤติกรรมของ Drupal ถูกเรียกใช้หลายครั้ง และต่อมาเราได้รับพฤติกรรมที่ไม่ได้ตั้งใจ

บริบทใน "บริบท Drupal" คืออะไร

  • เมื่อเรียกใช้วิธีการแนบสำหรับพฤติกรรมทั้งหมด Drupal จะส่งผ่านพารามิเตอร์ บริบท
  • พารามิเตอร์บริบทที่ส่งผ่านมักจะให้แนวคิดที่ดีขึ้นเกี่ยวกับองค์ประกอบ DOM ที่กำลังประมวลผล
  • ในระหว่างการโหลดหน้าเริ่มต้น นี่จะเป็นเอกสาร HTML ที่สมบูรณ์ ในระหว่างการเรียกครั้งต่อๆ ไป นี่จะเป็นเพียงองค์ประกอบที่ถูกเพิ่มลงในเพจหรือได้รับการแก้ไข

จะเพิ่มบริบทได้อย่างไร

ปัญหาก่อนหน้านี้สามารถแก้ไขได้โดยใช้พารามิเตอร์ บริบท ที่จัดทำโดย Drupal Behaviors ในกรณีนี้ ครั้งแรกที่โหลดหน้าเว็บ เราได้รับเอกสาร HTML ทั้งหมดเป็นบริบท และนั่นคือเมื่อเราแนบส่วนหัว สำหรับการดำเนินการต่อไป จะเป็นส่วนของโค้ดที่ได้รับผลกระทบจาก Drupal Behaviors และด้วยเหตุนี้ส่วนของโค้ดจึงถูกควบคุมอย่างปลอดภัย

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

มีพฤติกรรมแปลก ๆ อีกครั้งเมื่อเราคลิก โหลดเพิ่มเติม รายการอาหารที่โหลดในตอนแรกทำงานได้ดี แต่หลังจากคลิกที่โหลดเพิ่มเติม รายการใหม่จะได้รับการคลิกฟังและทำงานได้ตามปกติ แต่รายการที่โหลดในตอนแรกจะแนบผู้ฟังอีกครั้งและการคลิกที่รายการเหล่านั้นจะเรียกเหตุการณ์การคลิกมากกว่าหนึ่งครั้ง!

โหลดเพิ่มเติม

พฤติกรรม Drupal เริ่มทำงานผิดปกติเมื่อใด

  • เขียนตัวฟังเหตุการณ์ทั้งหมดภายในพฤติกรรม Drupal โดยไม่ต้องใช้ Once และ Context
  • การประกาศฟังก์ชันที่ไม่ต้องการภายในพฤติกรรมของ Drupal ซึ่งนำไปสู่การประกาศฟังก์ชันใหม่ทุกครั้งที่เรียกใช้เมธอดการแนบ

“ครั้งเดียว” เพื่อช่วยชีวิต

  • ตรวจสอบให้แน่ใจว่ามีการประมวลผลบางอย่างเพียงครั้งเดียวโดยเพิ่มแอตทริบิวต์ data-once ในองค์ประกอบ DOM หลังจากโค้ดถูกเรียกใช้
  • หากมีการเรียกใช้ลักษณะการทำงานอีกครั้ง อิลิเมนต์ที่มีแอตทริบิวต์ 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 กับองค์ประกอบที่แนบตัวฟังเหตุการณ์และองค์ประกอบที่โหลดใหม่และองค์ประกอบที่โหลดก่อนหน้านี้ทำงานอย่างถูกต้อง

ความจำเป็นในการแยกวิธีการ

วิธีการถอดทำหน้าที่เหมือนผู้ต่อต้านฮีโร่ (ไม่ใช่ผู้ชั่วร้าย) โดยลบสิ่งที่เราทำในวิธีการแนบ รหัสใดๆ ในวิธีการแยกจะถูกเรียกเมื่อใดก็ตามที่เนื้อหาถูกลบออกจาก DOM สิ่งนี้ช่วยให้เราสามารถล้างแอปพลิเคชันของเราได้ ตัวอย่างเช่น วิธีการแยกช่วยให้เราสามารถลบผู้ฟังเหตุการณ์ที่ไม่ต้องการซึ่งใช้ทรัพยากร เช่น สถานการณ์การสำรวจอย่างต่อเนื่อง

ตัวอย่างของการแยก

สมมติว่าเรามีแบบฟอร์ม 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:

แยกออก

เพื่อหลีกเลี่ยงสิ่งนี้ เราสามารถใช้วิธีการแยกดังนี้:

 (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 ที่จะช่วยคุณสร้างประสบการณ์เว็บแบบโต้ตอบ ใช้ประโยชน์จาก Drupal ให้ได้มากที่สุด? เราชอบที่จะพูดคุย!