Direct vs. Delegated - jQuery .on ()


159

ฉันพยายามที่จะเข้าใจความแตกต่างนี้โดยเฉพาะระหว่างโดยตรงและได้รับการแต่งตั้งจัดการเหตุการณ์โดยใช้jQuery .on ()วิธีการ ประโยคสุดท้ายในย่อหน้านี้โดยเฉพาะ:

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

การ "เรียกใช้ตัวจัดการสำหรับองค์ประกอบใด ๆ " หมายความว่าอย่างไร ฉันทำหน้าทดสอบเพื่อทดสอบด้วยแนวคิด แต่โครงสร้างทั้งสองต่อไปนี้นำไปสู่พฤติกรรมที่เหมือนกัน:

$("div#target span.green").on("click", function() {
   alert($(this).attr("class") + " is clicked");
});

หรือ,

$("div#target").on("click", "span.green", function() {
   alert($(this).attr("class") + " is clicked");
});

บางทีใครบางคนอาจอ้างถึงตัวอย่างที่แตกต่างเพื่อชี้แจงประเด็นนี้? ขอบคุณ


7
สำหรับผู้ที่สนใจ: jsperf.com/jquery-fn-on-delegate-vs-direct
dgo

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

1
"หน้าทดสอบ" ที่คุณอ้างอิงไม่ทำงาน
Jaime Montoya

คำตอบ:


373

กรณีที่ 1 (โดยตรง):

$("div#target span.green").on("click", function() {...});

== เฮ้! ฉันต้องการให้ span.green ภายใน div # target ฟังเพลงทั้งหมด: เมื่อคุณคลิกให้ทำ X

กรณีที่ 2 (มอบหมาย):

$("div#target").on("click", "span.green", function() {...});

== เฮ้ div # target! เมื่อองค์ประกอบลูกใด ๆ ของคุณที่ถูกคลิก "span.green" ให้ทำ X กับองค์ประกอบเหล่านั้น

กล่าวอีกนัยหนึ่ง ...

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

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


45
นั่นเป็นคำอธิบายที่ดีมากและได้นำความชัดเจนมาสู่ปัญหาที่ฉันปฏิเสธมานานแล้วที่จะเข้าใจ ขอบคุณ!
dgo

3
ดังนั้นทำไมไม่on()อนุญาตให้ทั้งสองมีปากเสียงเมื่อที่สวยมากจะเป็นเช่นเดียวกับการใช้click()?
ชาวญี่ปุ่น

5
.on () เป็น API วัตถุประสงค์ทั่วไปที่สามารถจัดการเหตุการณ์ประเภทใดก็ได้รวมถึงเหตุการณ์ที่แตกต่างกันหลายรายการ (คุณสามารถใส่ชื่อเหตุการณ์หลายชื่อในสตริงแรก) .click () เป็นเพียงชวเลขสำหรับรูปแบบแรกนั้น
N3dst4

1
@newbie, @ N3dst4: e.targetจะเป็นเป้าหมายเริ่มต้นของเหตุการณ์คลิก (อาจเป็นโหนดลูกถ้าspan.greenมีลูก) จากภายในตัวจัดการคุณควรใช้การthisอ้างอิง ดูซอนี้
LeGEC

1
หมายเหตุเพิ่มเติมเพื่อเพิ่มในหัวข้อของการมอบหมาย - มันทั้งมีประโยชน์มากและมีประสิทธิภาพมาก คุณจะใช้ทรัพยากรเบราว์เซอร์น้อยลงด้วยการมอบสิทธิ์และการแนบกิจกรรมกับองค์ประกอบที่ตรงกันทั้งหมด คิดว่าฉันจะพูดถึงมันในกรณีที่คนต้องการเหตุผลเพิ่มเติมเพื่อใช้การมอบหมาย
phatskat

6

วิธีแรก$("div#target span.green").on(), ผูกตัวจัดการการคลิกโดยตรงกับช่วงที่ตรงกับตัวเลือกในขณะที่รหัสจะถูกดำเนินการ ซึ่งหมายความว่าหากมีการเพิ่มช่วงอื่น ๆ ในภายหลัง (หรือเปลี่ยนคลาสให้ตรงกัน) พวกเขาพลาดและจะไม่มีตัวจัดการการคลิก นอกจากนี้ยังหมายความว่าหากคุณลบคลาส "สีเขียว" ออกจากหนึ่งในช่วงเวลาที่ตัวจัดการการคลิกจะทำงานต่อไป - jQuery จะไม่ติดตามการกำหนดตัวจัดการและตรวจสอบว่าตัวเลือกยังคงตรงกันอยู่หรือไม่

วิธีที่สอง$("div#target").on()ผูกตัวจัดการการคลิกให้กับ div ที่ตรงกัน (อีกครั้งนี่เป็นสิ่งที่ตรงกับที่ตรงกันในขณะนั้น) แต่เมื่อการคลิกเกิดขึ้นที่ใดที่หนึ่งใน div ฟังก์ชันตัวจัดการจะทำงานก็ต่อเมื่อการคลิก เกิดขึ้นไม่เพียง แต่ใน div แต่ในองค์ประกอบลูกที่ตรงกับตัวเลือกในพารามิเตอร์ที่สองถึง.on()"span.green" ทำแบบนี้ไม่สำคัญว่าจะสร้างช่วงเด็กเมื่อใดการคลิกที่พวกเขาจะยังคงเรียกใช้ตัวจัดการ

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


5

คำอธิบายของ N3dst4 นั้นสมบูรณ์แบบ จากสิ่งนี้เราสามารถสันนิษฐานได้ว่าองค์ประกอบของเด็กทั้งหมดอยู่ในร่างกายดังนั้นเราจึงจำเป็นต้องใช้สิ่งนี้:

$('body').on('click', '.element', function(){
    alert('It works!')
});

มันทำงานกับเหตุการณ์โดยตรงหรือผู้ได้รับมอบหมาย


2
jquery แนะนำให้ใช้เนื้อความเนื่องจากช้ากว่าเพราะสคริปต์จะต้องค้นหา childs ทั้งหมดในร่างกายซึ่งควรจะเป็นอย่างมากในกรณีส่วนใหญ่ จะดีกว่า (เร็วกว่า) เพื่อใช้คอนเทนเนอร์พาเรนต์กลางขององค์ประกอบ
mikesoft

1
Jquery ลบวิธีการถ่ายทอดสดซึ่งทำเช่นเดียวกับที่คุณแสดง นี่คือประสิทธิภาพที่อ่อนแอและไม่ควรใช้
Maciej Sikora

ในเบราว์เซอร์ที่ทันสมัยการ$('body').on()มอบหมายจาก.elementควรมีลักษณะเหมือนกับ Native document.body.addEventHandler()กับif (Event.target.className.matches(/\belement\b/))ในการติดต่อกลับ มันอาจจะช้าลงเล็กน้อยใน jquery เนื่องจาก$.proxyค่าใช้จ่าย แต่ไม่พูดกับฉันในที่
cowbert

2

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

  • .onที่ถูกผูกไว้หมายถึงสิ่งที่เหลืออยู่ของ
  • .on()เลือกหมายถึงข้อโต้แย้งที่ 2

การมอบหมายไม่ทำงานเหมือน. ค้นหา () เลือกชุดย่อยขององค์ประกอบที่ถูกผูกไว้ ตัวเลือกใช้กับองค์ประกอบลูกที่เข้มงวดเท่านั้น

$("span.green").on("click", ...

แตกต่างจาก

$("span").on("click", ".green", ...

โดยเฉพาะอย่างยิ่งที่จะได้รับข้อได้เปรียบ @ คำแนะนำ N3dst4 ที่มี "องค์ประกอบที่ถูกสร้างขึ้นในอนาคต" องค์ประกอบที่ถูกผูกไว้จะต้องเป็นผู้ปกครองถาวร จากนั้นเด็กที่เลือกสามารถมาและไป

แก้ไข

รายการตรวจสอบของสาเหตุที่ได้รับมอบหมาย.onไม่ทำงาน

เหตุผลหากินทำไม$('.bound').on('event', '.selected', some_function)ไม่ทำงาน:

  1. องค์ประกอบที่ถูกผูกไว้ไม่ถาวร มันถูกสร้างขึ้นหลังจากการโทร.on()
  2. องค์ประกอบที่เลือกไม่ใช่ลูกที่เหมาะสมขององค์ประกอบที่ถูกผูกไว้ มันเป็นองค์ประกอบเดียวกัน
  3. องค์ประกอบที่เลือกป้องกันไม่ให้เดือดปุด ๆ.stopPropagation()ของเหตุการณ์ไปยังองค์ประกอบที่ถูกผูกไว้โดยโทร

(ไม่ใช้เหตุผลที่ยุ่งยากน้อยเช่นตัวเลือกที่สะกดผิด)


1

ฉันจะโพสต์ด้วยการเปรียบเทียบกิจกรรมโดยตรงและมอบหมาย ฉันเปรียบเทียบ js บริสุทธิ์ แต่มีความหมายเหมือนกันสำหรับ jquery ที่ encapsulate เท่านั้น

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

สำหรับข้อมูลเพิ่มเติมและการเปรียบเทียบแบบเต็ม - http://maciejsikora.com/standard-events-vs-event-delegation/

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


หากการติดตั้งตัวจัดการเหตุการณ์จำนวนมาก (ตัวอย่างเช่นแต่ละแถวของตาราง) จะเป็นการดีกว่าหากใช้ตัวจัดการที่ได้รับมอบหมายเดียวกับคอนเทนเนอร์แทนการแนบตัวจัดการโดยตรงโดยตรงจำนวนมาก คุณสามารถเห็นสิ่งนี้จากข้อเท็จจริงพื้นฐานที่การนับตัวจัดการเหตุการณ์นั้นเป็นตัวชี้วัดการทำโปรไฟล์ด้วยตัวเอง
cowbert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.