ข้อมูลเบื้องต้นเกี่ยวกับกิจกรรมป๊อปอัป การทำงานขั้นสูงกับวัตถุเหตุการณ์ใน JavaScript การยกเลิกเหตุการณ์ bubbling js

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

บับเบิ้ลเหตุการณ์

หากมีเหตุการณ์เกิดขึ้นกับองค์ประกอบบางอย่าง องค์ประกอบนั้นจะเริ่ม "ป๊อปอัป" เช่น เกิดขึ้นในพ่อแม่ แล้วก็ในปู่ย่าตายาย ฯลฯ

ตามมาว่าเหตุการณ์ที่สร้างโดยองค์ประกอบบางอย่างสามารถดักจับได้โดยใช้ตัวจัดการบนพาเรนต์ ปู่ย่าตายาย ฯลฯ

เราจะสาธิตการเกิดขึ้นของเหตุการณ์ (ฟองสบู่) โดยใช้ตัวอย่างต่อไปนี้:

หัวเรื่อง

ข้อความที่สำคัญมากบางข้อความ

บท

ข้อความบางส่วน

ข้อความที่เหลือ

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

document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); สำหรับ (var i=0; i< allElements.length; i++) { allElements[i].addEventListener("click",function() {console.log(this.tagName);},false); }; document.addEventListener("click",function() {console.log(this);},false); window.addEventListener("click",function() {console.log(this);},false); });

มาสร้างหน้า HTML และแทรกโค้ด HTML ด้านบนลงไป สคริปต์ที่เขียนใน จาวาสคริปต์ให้แทรกก่อนแท็กเนื้อหาปิด หลังจากนั้นให้เปิดเพจที่สร้างขึ้นใหม่ในเว็บเบราว์เซอร์ กดปุ่ม F12 แล้วไปที่คอนโซล ตอนนี้เรามาคลิกซ้ายในพื้นที่ที่เป็นขององค์ประกอบที่แข็งแกร่งแล้วดูว่ากิจกรรมจะปรากฏขึ้นมาอย่างไร

วิธีขัดจังหวะเหตุการณ์เดือดปุด ๆ

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

ตัวอย่างเช่น ลองเปลี่ยนตัวอย่างของเราด้านบนเพื่อให้เหตุการณ์ไม่ปรากฏอยู่เหนือเนื้อหา: document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); for (var i=0 ; ฉัน

ไม่ต้องสงสัยเลยว่าพื้นผิวมีความสะดวกและโปร่งใสทางสถาปัตยกรรม อย่าหยุดมันเว้นแต่จำเป็นจริงๆ

รับองค์ประกอบที่เรียกว่าตัวจัดการ

เพื่อที่จะรับองค์ประกอบ DOM (วัตถุ) ที่เรียกว่าตัวจัดการเหตุการณ์ คุณต้องใช้คีย์ คำนี้. ที่ให้ไว้ คำสำคัญ(สิ่งนี้) ใช้ได้เฉพาะในตัวจัดการหากคุณสมัครรับกิจกรรมโดยใช้ JavaScript

ตัวอย่างเช่น เรามาแสดง id ขององค์ประกอบที่เรียกว่าตัวจัดการเหตุการณ์ในคอนโซล:

วาร์ myP = document.getElementById("myP"); myP.addEventListener("click",function())( //get the DOM element that called the event handler - this //get it its id and output it to the console console.log(this.id); ));

คุณยังสามารถใช้คุณสมบัติ currentTarget (event.currentTarget) เพื่อรับองค์ประกอบปัจจุบัน

ขั้นตอน (ระยะ) ของเหตุการณ์

ก่อนที่เหตุการณ์จะเริ่มเกิดขึ้น (ระยะการขึ้น) ก่อนอื่นจะต้องผ่านขั้นตอนเพิ่มเติมอีก 2 ขั้นตอน:

  • ขั้นที่ 1 คือขั้นของการดื่มด่ำกับองค์ประกอบที่ทำให้เกิดเหตุการณ์ เหล่านั้น. ในระยะนี้จะมีการเคลื่อนตัวจากบนลงล่างคือ จาก วัตถุหน้าต่างถึงองค์ประกอบ ระยะนี้เรียกอีกอย่างว่าระยะสกัดกั้น
  • ระยะที่ 2 คือระยะของการบรรลุเป้าหมาย เช่น องค์ประกอบ (วัตถุ) ที่สร้างเหตุการณ์

เมื่อพิจารณาถึงขั้นตอนทั้งหมดที่เหตุการณ์ดำเนินไป จะเกิดภาพต่อไปนี้:

มาแก้ไขสคริปต์ตัวอย่างข้างต้นดังนี้:

Document.addEventListener("DOMContentLoaded", function() ( var allElements = document.getElementsByTagName("*"); สำหรับ (var i=0; i

พารามิเตอร์ตัวที่สามของเมธอด addEventListener และ RemoveEventListener จะกำหนดระยะที่เหตุการณ์จะถูกตรวจจับ ถ้า พารามิเตอร์นี้ถูกตั้งค่าเป็น true เหตุการณ์จะถูกดักจับที่ระยะเหตุการณ์แช่ (สกัดกั้น) และหากพารามิเตอร์เป็น false เหตุการณ์จะถูกดักจับที่ระยะฟองสบู่ ในการจัดการเหตุการณ์บนเป้าหมาย คุณสามารถใช้เมธอด addEventListener ได้เช่นเดียวกัน ค่าเท็จและมีค่าเป็นจริง

ข้อควรสนใจ: ในระหว่างขั้นตอนการแช่ (สกัดกั้น) เหตุการณ์จะถูกดักจับโดยตัวจัดการที่เพิ่มโดยใช้เมธอด addEventListener() เท่านั้น ตัวจัดการถูกเพิ่มโดยใช้วิธีอื่น ( แอตทริบิวต์ HTMLหรือผ่าน JavaScript โดยใช้คุณสมบัติ on[event]) สามารถสกัดกั้นเหตุการณ์ได้เฉพาะในขั้นตอนที่เดือดปุด ๆ

รับองค์ประกอบที่สร้างเหตุการณ์

เพื่อให้ได้องค์ประกอบเป้าหมายเช่น องค์ประกอบที่สร้างเหตุการณ์ต้องใช้คุณสมบัติเป้าหมาย (event.target)

ลองพิจารณาตัวอย่างข้างต้น ซึ่งเราเปลี่ยนเนื้อหาขององค์ประกอบสคริปต์ดังต่อไปนี้:

Document.addEventListener("DOMContentLoaded", function() ( var elementBody = document.body; elementBody.addEventListener("click",function())( console.log(this.tagName + " - องค์ประกอบที่เรียกว่าตัวจัดการ") ; console .log(event.currentTarget.tagName + " - องค์ประกอบที่เรียกว่าตัวจัดการ"); console.log(event.target.tagName + " - องค์ประกอบที่สร้างเหตุการณ์"); ),false); )) ;

เรามาสาธิตตัวอย่างของเราโดยคลิกซ้ายในพื้นที่ที่เป็นขององค์ประกอบที่แข็งแกร่ง:

ทุกอย่างเริ่มต้นด้วยการใช้ JavaScript และคลาส

อย่างไรก็ตาม ฉันมีปัญหา ฉันต้องการใช้สิ่งที่เรียกว่า Bubble Events แต่ฉันก็ต้องการลดการพึ่งพาที่ฉันต้องแทรกให้เหลือน้อยที่สุดด้วย ฉันไม่ต้องการเชื่อมต่อ ไลบรารี jQueryสำหรับ "การทดสอบเล็กๆ น้อยๆ นี้" เพียงเพื่อใช้กิจกรรมป๊อปอัป

เรามาดูรายละเอียดกันดีกว่าว่ากิจกรรม toasting คืออะไร ทำงานอย่างไร และมีวิธีนำไปใช้อย่างไรบ้าง

โอเค แล้วมีปัญหาอะไรล่ะ? ลองดูตัวอย่างง่ายๆ:

สมมติว่าเรามีรายการปุ่มต่างๆ ทุกครั้งที่ฉันคลิกที่หนึ่งในนั้น มันควรจะกลายเป็น "ใช้งานอยู่" หลังจากกดอีกครั้งปุ่มจะกลับสู่สถานะเดิม

เริ่มต้นด้วย HTML:

  • ดินสอ
  • ปากกา
  • ยางลบ

ฉันสามารถใช้ตัวจัดการเหตุการณ์ JavaScript มาตรฐานเช่นนี้:

สำหรับ(var i = 0; i< buttons.length; i++) { var button = buttons[i]; button.addEventListener("click", function() { if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); }); }
ดูดี... แต่มันใช้งานไม่ได้ โดย อย่างน้อยไม่ใช่อย่างที่เราคาดหวัง

การปิดชนะ สำหรับผู้ที่รู้ JavaScript เพียงเล็กน้อยปัญหาก็ชัดเจน

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

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

สิ่งที่เราต้องการคือบริบทที่แยกจากกันสำหรับแต่ละฟังก์ชัน:

ปุ่ม Var = document.querySelectorAll(".toolbar button"); var createToolbarButtonHandler = function(ปุ่ม) ( return function() ( if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active"); ); ); สำหรับ (var i = 0; i< buttons.length; i++) { buttons[i].addEventListener("click", createToolBarButtonHandler(buttons[i])); }
ดีขึ้นมาก! และที่สำคัญที่สุดคือมันทำงานได้อย่างถูกต้อง เราได้สร้างฟังก์ชัน createToolbarButtonHandle ที่ส่งคืนตัวจัดการเหตุการณ์ จากนั้นเราจะแนบตัวจัดการของเราเองสำหรับแต่ละปุ่ม

แล้วปัญหาคืออะไร? มันดูดีและใช้งานได้ดี อย่างไรก็ตาม เรายังสามารถทำให้โค้ดของเราดีขึ้นได้

อันดับแรก เราสร้างตัวจัดการมากเกินไป สำหรับแต่ละปุ่มภายใน .toolbar เราจะสร้างฟังก์ชันและผูกมันไว้เป็นตัวจัดการเหตุการณ์ สำหรับปุ่มสามปุ่ม การใช้งานหน่วยความจำนั้นน้อยมาก

แต่ถ้าเรามีสิ่งเช่นนี้:

  • ฟู
  • บาร์
  • // ...อีก 997 องค์ประกอบ...
  • บาซ

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

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

ออบเจ็กต์เหตุการณ์มีข้อมูลบางอย่างเกี่ยวกับเหตุการณ์ ในกรณีของเรา เราสนใจฟิลด์ currentTarget จากนั้นเราจะได้ลิงก์ไปยังองค์ประกอบที่ถูกคลิก:

แถบเครื่องมือ VarButtonHandler = function(e) ( ปุ่ม var = e.currentTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); ); สำหรับ (var i = 0; i< buttons.length; i++) { button.addEventListener("click", toolbarButtonHandler); }
ยอดเยี่ยม! ไม่เพียงแต่เราทำให้ทุกอย่างง่ายขึ้นเหลือเพียงฟังก์ชันเดียวที่ใช้หลายครั้งเท่านั้น เรายังทำให้โค้ดของเราอ่านง่ายขึ้นโดยการลบฟังก์ชันตัวสร้างที่ไม่จำเป็นออก

อย่างไรก็ตาม เรายังสามารถทำได้ดีกว่านี้

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

อาจมีแนวทางอื่นอีกหรือไม่?

มาเริ่มด้วยการทำความเข้าใจว่าเหตุการณ์ทำงานอย่างไรและดำเนินไปอย่างไรตาม DOM ของเรา

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

    เหตุการณ์ถูกสร้างขึ้นภายนอกเอกสาร จากนั้นจึงย้ายตามลำดับชั้น DOM ไปยังองค์ประกอบเป้าหมาย เมื่อบรรลุเป้าหมายแล้ว เหตุการณ์จะถูกดึงมาจากองค์ประกอบ DOM ในลักษณะเดียวกัน

    นี่คือเทมเพลต HTML ของเรา:

    • ปุ่ม A
    • ปุ่มบี
    • ปุ่ม C

    เมื่อผู้ใช้คลิกที่ปุ่ม A เหตุการณ์จะดำเนินไปดังนี้:

    เริ่ม
    | #เอกสาร
    | ขั้นตอนการสกัดกั้น
    | HTML
    | ร่างกาย
    | แอล
    | ลี#li_1
    | ปุ่ม A< - Событие возникает для целевого элемента
    | ระยะขึ้น
    | ลี#li_1
    | แอล
    | ร่างกาย
    | HTML
    วี#เอกสาร

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

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

    ลองใช้แถบเครื่องมือของเราเป็นตัวอย่างที่เป็นรูปธรรม:

    ชั้น Ul = "แถบเครื่องมือ">

  • ดินสอ
  • ปากกา
  • ยางลบ

  • ตอนนี้เรารู้แล้วว่าการคลิกปุ่มใด ๆ จะปรากฏขึ้นผ่านองค์ประกอบ ul.toolbar ให้แนบตัวจัดการเหตุการณ์ของเราไปกับมัน โชคดีที่เรามีมันอยู่แล้ว:

    แถบเครื่องมือ Var = document.querySelector(".toolbar"); toolbar.addEventListener("คลิก", ​​function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. ลบ("ใช้งานอยู่"); ));
    ตอนนี้เรามีโค้ดที่สะอาดกว่ามาก และเรายังกำจัดการวนซ้ำอีกด้วย! อย่างไรก็ตาม โปรดทราบว่าเราได้แทนที่ e.currentTarget ด้วย e.target เหตุผลก็คือเราประมวลผลกิจกรรมในระดับที่แตกต่างกัน

    e.target คือเป้าหมายที่แท้จริงของเหตุการณ์ โดยที่เหตุการณ์จะผ่าน DOM และที่ที่เหตุการณ์จะเกิดขึ้น
    e.currentTarget - องค์ประกอบปัจจุบันที่จัดการเหตุการณ์ ในกรณีของเรา นี่คือ ul.toolbar

    ปรับปรุงกิจกรรมบับเบิ้ล ช่วงเวลานี้เราจัดการการคลิกแต่ละองค์ประกอบที่ปรากฏขึ้นผ่าน ul.toolbar แต่เงื่อนไขการตรวจสอบของเราง่ายเกินไป จะเกิดอะไรขึ้นถ้าเรามี DOM ที่ซับซ้อนมากขึ้นซึ่งมีไอคอนและองค์ประกอบที่ไม่ได้ออกแบบมาให้คลิกได้

    • ดินสอ
    • ปากกา
    • ยางลบ

    อ๊ะ! ตอนนี้เมื่อเราคลิกที่ li.separator หรือไอคอน เราจะเพิ่มคลาส .active ลงไป อย่างน้อยที่สุดนี่ก็ไม่ดี เราต้องการวิธีกรองเหตุการณ์เพื่อให้เราตอบสนองต่อองค์ประกอบที่เราต้องการ

    มาสร้างฟังก์ชันตัวช่วยเล็กๆ ขึ้นมาสำหรับสิ่งนี้:

    Var delegate = function(criteria, Listener) ( return function(e) ( var el = e.target; do ( if (!criteria(el)) Continue; e.delegateTarget = el; Listener.apply(this, arguments); กลับ; ) ในขณะที่ ((el = el.parentNode)); ); );
    ผู้ช่วยของเราทำสองสิ่ง ขั้นแรก มันจะวนซ้ำแต่ละองค์ประกอบและพาเรนต์ของมัน และตรวจสอบว่าเป็นไปตามเงื่อนไขที่ส่งในพารามิเตอร์เกณฑ์หรือไม่ หากองค์ประกอบเป็นไปตามที่ต้องการ ตัวช่วยจะเพิ่มฟิลด์ให้กับออบเจ็กต์เหตุการณ์ที่เรียกว่า delegateTarget ซึ่งจะจัดเก็บองค์ประกอบที่ตรงตามเงื่อนไขของเรา แล้วจึงเรียกผู้ดูแล ดังนั้น หากไม่มีองค์ประกอบใดที่ตรงตามเงื่อนไข ก็จะไม่มีการเรียกตัวจัดการ

    เราสามารถใช้มันได้ดังนี้:

    แถบเครื่องมือ Var = document.querySelector(".toolbar"); var ButtonFilter = function(elem) ( return elem.classList && elem.classList.contains("btn"); ); var buttonHandler = function(e) ( ปุ่ม var = e.delegateTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); ); toolbar.addEventListener("คลิก", ​​มอบหมาย (buttonsFilter, buttonHandler));
    สิ่งที่แพทย์สั่ง: ตัวจัดการเหตุการณ์หนึ่งตัวแนบกับองค์ประกอบเดียวที่ทำงานทั้งหมด แต่มันทำเฉพาะกับองค์ประกอบที่เราต้องการเท่านั้น และตอบสนองอย่างสมบูรณ์แบบต่อการเพิ่มและการลบวัตถุออกจาก DOM

    สรุป เราได้ตรวจสอบพื้นฐานของการดำเนินการกิจกรรมการมอบหมาย (การจัดการป๊อปอัป) โดยย่อแล้ว จาวาสคริปต์บริสุทธิ์. นี่เป็นสิ่งที่ดีเพราะเราไม่จำเป็นต้องสร้างและแนบตัวจัดการจำนวนมากสำหรับแต่ละองค์ประกอบ

    หากฉันต้องการสร้างไลบรารี่หรือใช้โค้ดในการพัฒนา ฉันจะเพิ่มบางสิ่ง:

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

    เกณฑ์ Var = ( isElement: function(e) ( return e instanceof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return allowance.isElement(e) && e.classList.contains(cls); ) ) //เกณฑ์เพิ่มเติม);
    การใช้ผู้ช่วยบางส่วนก็มีประโยชน์เช่นกัน:

    Var partialDelgate = function(criteria) ( return function(handler) ( return delgate(criteria, handler); ) );
    บทความต้นฉบับ: การทำความเข้าใจเหตุการณ์ JavaScript ที่ได้รับมอบหมาย
    (จากผู้แปล: คนแรกของฉัน ตัดสินอย่างเคร่งครัด)

    ขอให้มีความสุขในการเขียนโค้ด!

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

    ข้อกำหนดเบื้องต้น: วัตถุประสงค์:
    ความรู้คอมพิวเตอร์ขั้นพื้นฐาน ความเข้าใจพื้นฐานเกี่ยวกับ HTML และ CSS ขั้นตอนแรก JavaScript
    เพื่อทำความเข้าใจทฤษฎีพื้นฐานของเหตุการณ์ วิธีการทำงานของเบราว์เซอร์ และเหตุการณ์อาจแตกต่างกันในสภาพแวดล้อมการเขียนโปรแกรมต่างๆ
    ลำดับเหตุการณ์แห่งความโชคดี

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

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

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

    คุณสามารถรวบรวมจากสิ่งนี้ (และจากการดูข้อมูลอ้างอิงกิจกรรม MDN) ว่ามีกิจกรรมมากมายที่สามารถตอบสนองได้

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

    หมายเหตุ: กิจกรรมบนเว็บไม่ได้เป็นส่วนหนึ่งของภาษา JavaScript หลัก แต่ถูกกำหนดให้เป็นส่วนหนึ่งของ API ที่สร้างไว้ในเบราว์เซอร์

    ตัวอย่างง่ายๆ

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

    เปลี่ยนสี

    ปุ่ม (ระยะขอบ: 10px );

    JavaScript มีลักษณะดังนี้:

    Const btn = document.querySelector("ปุ่ม"); ฟังก์ชั่นสุ่ม(ตัวเลข) ( return Math.floor(Math.random() * (number+1)); ) btn.onclick = function() ( const rndCol = "rgb(" + Random(255) + "," + สุ่ม(255) + "," + สุ่ม(255) + ")"; document.body.style.พื้นหลังสี = rndCol; )

    ในโค้ดนี้ เราจัดเก็บการอ้างอิงไปยังปุ่มภายในค่าคงที่ที่เรียกว่า btn โดยใช้ฟังก์ชัน Document.querySelector() นอกจากนี้เรายังกำหนดฟังก์ชันที่ส่งคืนตัวเลขสุ่มด้วย ส่วนที่สามของโค้ดคือตัวจัดการเหตุการณ์ ค่าคงที่ btn ชี้ไปที่องค์ประกอบ และออบเจ็กต์ประเภทนี้มีจำนวนเหตุการณ์ที่สามารถเริ่มทำงานได้ ดังนั้นจึงมีตัวจัดการเหตุการณ์ที่พร้อมใช้งาน เรากำลังรับฟังเหตุการณ์การคลิกโดยการตั้งค่าคุณสมบัติตัวจัดการเหตุการณ์ onclick ให้เท่ากับฟังก์ชันที่ไม่ระบุชื่อซึ่งมีโค้ดที่สร้างสี RGB แบบสุ่มและตั้งค่าสีพื้นหลังให้เท่ากับสีนั้น

    รหัสนี้จะถูกรันเมื่อใดก็ตามที่เหตุการณ์การคลิกเริ่มทำงานในองค์ประกอบ นั่นคือเมื่อใดก็ตามที่ผู้ใช้คลิกที่องค์ประกอบนั้น

    ผลลัพธ์ตัวอย่างจะเป็นดังนี้:

    มันไม่ใช่แค่หน้าเว็บเท่านั้น

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

    ตัวจัดการเหตุการณ์แบบอินไลน์ - อย่าใช้สิ่งเหล่านี้

    คุณอาจเห็นรูปแบบเช่นนี้ในโค้ดของคุณ:

    กดฟังก์ชัน bgChange() ให้ฉัน ( const rndCol = "rgb(" + Random(255) + "," + Random(255) + "," + Random(255) + ")"; document.body.style.wallpaperColor = rndCol; )

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

    กดฉันสิ

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

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

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

    ปุ่ม Const = document.querySelectorAll("ปุ่ม"); สำหรับ (ให้ i = 0; i< buttons.length; i++) { buttons[i].onclick = bgChange; } buttons.forEach(function(button) { button.onclick = bgChange; });

    หมายเหตุ: การแยกตรรกะการเขียนโปรแกรมออกจากเนื้อหายังช่วยให้ไซต์ของคุณเป็นมิตรกับเครื่องมือค้นหามากขึ้นอีกด้วย

    addEventListener() และ RemoveEventListener()

    กลไกเหตุการณ์ประเภทใหม่ล่าสุดถูกกำหนดไว้ใน Document Object Model (DOM) Level 2 Events Specification ซึ่งจัดเตรียมฟังก์ชันใหม่ให้กับเบราว์เซอร์ - addEventListener() ฟังก์ชั่นนี้มีลักษณะคล้ายกับคุณสมบัติตัวจัดการเหตุการณ์ แต่ไวยากรณ์แตกต่างอย่างเห็นได้ชัด เราสามารถเขียนตัวอย่างสีแบบสุ่มของเราใหม่ให้มีลักษณะดังนี้:

    Const btn = document.querySelector("ปุ่ม"); ฟังก์ชั่น bgChange() ( const rndCol = "rgb(" + สุ่ม(255) + "," + สุ่ม(255) + "," + สุ่ม(255) + ")"; document.body.style.พื้นหลังสี = rndCol; ) btn.addEventListener("คลิก", ​​bgChange);

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

    Btn.addEventListener("คลิก", ​​function() ( var rndCol = "rgb(" + สุ่ม(255) + "," + สุ่ม(255) + "," + สุ่ม(255) + ")"; document.body .style.พื้นหลังสี = rndCol; ));

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

    Btn.removeEventListener("คลิก", ​​bgChange);

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

    ประการที่สอง คุณยังสามารถลงทะเบียนตัวจัดการหลายตัวสำหรับ Listener เดียวกันได้ ตัวจัดการสองตัวต่อไปนี้จะไม่ถูกนำไปใช้ทั้งคู่:

    MyElement.onclick = functionA; myElement.onclick = functionB;

    บรรทัดที่สองเขียนทับค่าของ onclick ที่กำหนดโดยบรรทัดแรก สิ่งนี้จะได้ผลอย่างไรก็ตาม:

    MyElement.addEventListener("คลิก", ​​functionA); myElement.addEventListener("คลิก", ​​functionB);

    ตอนนี้ทั้งสองฟังก์ชันจะทำงานเมื่อมีการคลิกองค์ประกอบ

    นอกจากนี้ยังมีฟีเจอร์และตัวเลือกอันทรงพลังอื่นๆ อีกมากมายที่มีให้ในกลไกกิจกรรมนี้ บทความนี้อยู่นอกเหนือขอบเขตเล็กน้อย แต่หากคุณต้องการอ่านข้อมูลเพิ่มเติม โปรดดูที่หน้าอ้างอิง addEventListener() และ RemoveEventListener()

    ฉันควรใช้กลไกอะไร?

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

    อีกสองรายการค่อนข้างใช้แทนกันได้ อย่างน้อยก็สำหรับการใช้งานง่ายๆ:

    • คุณสมบัติตัวจัดการเหตุการณ์มีพลังและตัวเลือกน้อยกว่า แต่มีความเข้ากันได้ข้ามเบราว์เซอร์ที่ดีกว่า (ได้รับการสนับสนุนจนถึงตอนนี้) อินเทอร์เน็ตเอ็กซ์พลอเรอร์ 8). คุณควรเริ่มด้วยสิ่งเหล่านี้ในขณะที่คุณกำลังเรียนรู้
    • เหตุการณ์ DOM ระดับ 2 (addEventListener() ฯลฯ) มีประสิทธิภาพมากกว่า แต่ก็มีความซับซ้อนมากขึ้นและได้รับการรองรับน้อยกว่า (รองรับย้อนกลับไปถึง Internet Explorer 9) คุณควรทดลองใช้สิ่งเหล่านี้และตั้งใจที่จะใช้มันเมื่อเป็นไปได้

    ข้อดีหลักของกลไกที่สามคือคุณสามารถลบโค้ดตัวจัดการเหตุการณ์ได้หากจำเป็น โดยใช้ RemoveEventListener() และคุณสามารถเพิ่ม Listener ประเภทเดียวกันหลายรายการลงในองค์ประกอบได้หากจำเป็น ตัวอย่างเช่น คุณสามารถเรียก addEventListener("click", function() ( ... )) บนองค์ประกอบได้หลายครั้ง โดยมีการระบุฟังก์ชันที่แตกต่างกันในอาร์กิวเมนต์ที่สอง สิ่งนี้เป็นไปไม่ได้สำหรับคุณสมบัติตัวจัดการเหตุการณ์ เนื่องจากความพยายามในการตั้งค่าคุณสมบัติในภายหลังจะเขียนทับคุณสมบัติก่อนหน้านี้ เช่น:

    องค์ประกอบ.onclick = function1; องค์ประกอบ.onclick = function2; ฯลฯ

    หมายเหตุ: หากคุณถูกเรียกให้สนับสนุนเบราว์เซอร์ที่เก่ากว่า Internet Explorer 8 ในการทำงานของคุณ คุณอาจประสบปัญหา เนื่องจากเบราว์เซอร์รุ่นเก่าใช้โมเดลเหตุการณ์ที่แตกต่างจากเบราว์เซอร์รุ่นใหม่ แต่อย่ากลัวเลย ไลบรารี JavaScript ส่วนใหญ่ (เช่น jQuery) มีฟังก์ชันในตัวที่แยกความแตกต่างระหว่างเบราว์เซอร์ออกไป อย่ากังวลเรื่องนี้มากเกินไปในขั้นตอนนี้บนเส้นทางการเรียนรู้ของคุณ

    แนวคิดเหตุการณ์อื่นๆ

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

    วัตถุเหตุการณ์

    บางครั้งภายในฟังก์ชันตัวจัดการเหตุการณ์ คุณอาจเห็นพารามิเตอร์ที่ระบุด้วยชื่อ เช่น event , evt หรือเพียงแค่ e สิ่งนี้เรียกว่าวัตถุเหตุการณ์ และจะถูกส่งผ่านไปยังตัวจัดการเหตุการณ์โดยอัตโนมัติเพื่อให้มีคุณสมบัติและข้อมูลพิเศษ ตัวอย่างเช่น ลองเขียนตัวอย่างสีแบบสุ่มของเราอีกครั้งเล็กน้อย:

    ฟังก์ชัน bgChange(e) ( const rndCol = "rgb(" + Random(255) + "," + Random(255) + "," + Random(255) + ")"; e.target.style.พื้นหลังสี = rndCol ; console.log(e); ) btn.addEventListener("คลิก", ​​bgChange);

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

    หมายเหตุ : คุณสามารถใช้ชื่อใดก็ได้ที่คุณต้องการสำหรับออบเจ็กต์เหตุการณ์ คุณเพียงแค่ต้องเลือกชื่อที่คุณสามารถใช้เพื่ออ้างอิงภายในฟังก์ชันตัวจัดการเหตุการณ์ e/evt/event ถูกใช้บ่อยที่สุดโดยนักพัฒนาเพราะมันสั้นและง่ายต่อการจดจำ เป็นการดีเสมอที่จะรักษาความสม่ำเสมอ ทั้งกับตัวเองและกับคนอื่นๆ หากเป็นไปได้

    e.target มีประโยชน์อย่างเหลือเชื่อเมื่อคุณต้องการตั้งค่าตัวจัดการเหตุการณ์เดียวกันบนหลายองค์ประกอบ และดำเนินการบางอย่างกับองค์ประกอบทั้งหมดเมื่อมีเหตุการณ์เกิดขึ้น ตัวอย่างเช่น คุณอาจมีชุดไทล์ 16 ไทล์ที่หายไปเมื่อคลิก การสามารถตั้งค่าให้สิ่งที่หายไปเป็น e.target ได้เสมอ แทนที่จะต้องเลือกด้วยวิธีที่ยากกว่านี้จะมีประโยชน์มาก ในตัวอย่างต่อไปนี้ (ดูที่เป็นประโยชน์-eventtarget.html สำหรับซอร์สโค้ดแบบเต็ม และเห็นซอร์สโค้ดที่ทำงานอยู่ที่นี่ด้วย) เราสร้างองค์ประกอบ 16 รายการโดยใช้ JavaScript จากนั้นเราเลือกทั้งหมดโดยใช้ document.querySelectorAll() จากนั้นวนซ้ำแต่ละอัน โดยเพิ่มตัวจัดการ onclick ให้กับแต่ละอันซึ่งทำให้สีสุ่มถูกนำไปใช้กับแต่ละอันเมื่อคลิก:

    Const divs = document.querySelectorAll("div"); สำหรับ (ให้ i = 0; i< divs.length; i++) { divs[i].onclick = function(e) { e.target.style.backgroundColor = bgChange(); } }

    ผลลัพธ์มีดังนี้ (ลองคลิกดู - ขอให้สนุก):

    ตัวอย่างที่ซ่อน ตัวอย่างเป้าหมายเหตุการณ์ที่มีประโยชน์ div ( height: 100px; width: 25%; float: left; ) for (let i = 1; i
  • ดินสอ
  • ปากกา
  • ยางลบ

  • ตอนนี้เรารู้แล้วว่าการคลิกปุ่มใด ๆ จะปรากฏขึ้นผ่านองค์ประกอบ ul.toolbar ให้แนบตัวจัดการเหตุการณ์ของเราไปกับมัน โชคดีที่เรามีมันอยู่แล้ว:

    แถบเครื่องมือ Var = document.querySelector(".toolbar"); toolbar.addEventListener("คลิก", ​​function(e) ( var button = e.target; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList. ลบ("ใช้งานอยู่"); ));
    ตอนนี้เรามีโค้ดที่สะอาดกว่ามาก และเรายังกำจัดการวนซ้ำอีกด้วย! อย่างไรก็ตาม โปรดทราบว่าเราได้แทนที่ e.currentTarget ด้วย e.target เหตุผลก็คือเราประมวลผลกิจกรรมในระดับที่แตกต่างกัน

    e.target คือเป้าหมายที่แท้จริงของเหตุการณ์ โดยที่เหตุการณ์จะผ่าน DOM และที่ที่เหตุการณ์จะเกิดขึ้น
    e.currentTarget - องค์ประกอบปัจจุบันที่จัดการเหตุการณ์ ในกรณีของเรา นี่คือ ul.toolbar

    ปรับปรุงเหตุการณ์ป๊อปอัป ขณะนี้เราจัดการกับการคลิกแต่ละองค์ประกอบที่ปรากฏขึ้นผ่าน ul.toolbar แต่เงื่อนไขการตรวจสอบของเราง่ายเกินไป จะเกิดอะไรขึ้นถ้าเรามี DOM ที่ซับซ้อนมากขึ้นซึ่งมีไอคอนและองค์ประกอบที่ไม่ได้ออกแบบมาให้คลิกได้

    • ดินสอ
    • ปากกา
    • ยางลบ

    อ๊ะ! ตอนนี้เมื่อเราคลิกที่ li.separator หรือไอคอน เราจะเพิ่มคลาส .active ลงไป อย่างน้อยที่สุดนี่ก็ไม่ดี เราต้องการวิธีกรองเหตุการณ์เพื่อให้เราตอบสนองต่อองค์ประกอบที่เราต้องการ

    มาสร้างฟังก์ชันตัวช่วยเล็กๆ ขึ้นมาสำหรับสิ่งนี้:

    Var delegate = function(criteria, Listener) ( return function(e) ( var el = e.target; do ( if (!criteria(el)) Continue; e.delegateTarget = el; Listener.apply(this, arguments); กลับ; ) ในขณะที่ ((el = el.parentNode)); ); );
    ผู้ช่วยของเราทำสองสิ่ง ขั้นแรก มันจะวนซ้ำแต่ละองค์ประกอบและพาเรนต์ของมัน และตรวจสอบว่าเป็นไปตามเงื่อนไขที่ส่งในพารามิเตอร์เกณฑ์หรือไม่ หากองค์ประกอบเป็นไปตามที่ต้องการ ตัวช่วยจะเพิ่มฟิลด์ให้กับออบเจ็กต์เหตุการณ์ที่เรียกว่า delegateTarget ซึ่งจะจัดเก็บองค์ประกอบที่ตรงตามเงื่อนไขของเรา แล้วจึงเรียกผู้ดูแล ดังนั้น หากไม่มีองค์ประกอบใดที่ตรงตามเงื่อนไข ก็จะไม่มีการเรียกตัวจัดการ

    เราสามารถใช้มันได้ดังนี้:

    แถบเครื่องมือ Var = document.querySelector(".toolbar"); var ButtonFilter = function(elem) ( return elem.classList && elem.classList.contains("btn"); ); var buttonHandler = function(e) ( ปุ่ม var = e.delegateTarget; if(!button.classList.contains("active")) button.classList.add("active"); else button.classList.remove("active" ); ); toolbar.addEventListener("คลิก", ​​มอบหมาย (buttonsFilter, buttonHandler));
    สิ่งที่แพทย์สั่ง: ตัวจัดการเหตุการณ์หนึ่งตัวแนบกับองค์ประกอบเดียวที่ทำงานทั้งหมด แต่มันทำเฉพาะกับองค์ประกอบที่เราต้องการเท่านั้น และตอบสนองอย่างสมบูรณ์แบบต่อการเพิ่มและการลบวัตถุออกจาก DOM

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

    หากฉันต้องการสร้างไลบรารี่หรือใช้โค้ดในการพัฒนา ฉันจะเพิ่มบางสิ่ง:

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

    เกณฑ์ Var = ( isElement: function(e) ( return e instanceof HTMLElement; ), hasClass: function(cls) ( return function(e) ( return allowance.isElement(e) && e.classList.contains(cls); ) ) //เกณฑ์เพิ่มเติม);
    การใช้ผู้ช่วยบางส่วนก็มีประโยชน์เช่นกัน:

    Var partialDelgate = function(criteria) ( return function(handler) ( return delgate(criteria, handler); ) );
    บทความต้นฉบับ: การทำความเข้าใจเหตุการณ์ JavaScript ที่ได้รับมอบหมาย
    (จากผู้แปล: คนแรกของฉัน ตัดสินอย่างเคร่งครัด)

    ขอให้มีความสุขในการเขียนโค้ด!

    สวัสดี! ในบทเรียนนี้ ฉันต้องการพูดถึงแนวคิดที่สำคัญ เช่น การแสดงและการสกัดกั้นเหตุการณ์ Bubbling เป็นปรากฏการณ์ที่หากคุณคลิกที่องค์ประกอบย่อย เหตุการณ์จะเผยแพร่ไปยังองค์ประกอบระดับบนสุด

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

    ตัวจัดการนี้จะเริ่มทำงานหากคุณคลิกที่แท็กย่อยหรือ:

    คลิกที่ EM ตัวจัดการบน DIV จะทำงาน

    อย่างที่คุณเห็น เมื่อคุณคลิกที่องค์ประกอบ em ที่ซ้อนกัน ตัวจัดการบน div จะถูกทริกเกอร์ ทำไมสิ่งนี้ถึงเกิดขึ้น? อ่านต่อและค้นหา

    ขึ้น

    ดังนั้นหลักการพื้นฐานของการขึ้น:

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

    ตัวอย่างเช่น สมมติว่ามีองค์ประกอบที่ซ้อนกัน 3 รายการ FORM > DIV > P โดยมีตัวจัดการเหตุการณ์ในแต่ละองค์ประกอบ:

    body * ( ระยะขอบ: 10px; เส้นขอบ: 1px สีน้ำเงินทึบ; ) แบบฟอร์ม DIV

    Bubbling ช่วยให้มั่นใจได้ว่าการคลิกที่องค์ประกอบภายใน

    จะเรียกตัวจัดการคลิก (ถ้ามีแน่นอน) ก่อนตามจริง

    กระบวนการนี้เรียกว่าการขึ้น เนื่องจากเหตุการณ์ต่างๆ ดูเหมือนจะ "ลอยขึ้น" จากองค์ประกอบภายในขึ้นไปผ่านพ่อแม่ เช่นเดียวกับฟองอากาศที่ลอยอยู่ในน้ำ ดังนั้นคุณจึงสามารถค้นหาคำจำกัดความของฟองสบู่ได้เช่นกัน มันมาจากภาษาอังกฤษเท่านั้น คำเดือด - ลอยขึ้น

    การเข้าถึงองค์ประกอบเป้าหมาย event.target

    เพื่อที่จะค้นหาว่าองค์ประกอบใดที่เราตรวจพบเหตุการณ์นี้หรือเหตุการณ์นั้น จึงมีเมธอด event.target (อ่านเกี่ยวกับวัตถุเหตุการณ์)

    • event.target เป็นองค์ประกอบแหล่งที่มาจริงที่เกิดเหตุการณ์ขึ้น
    • นี่คือองค์ประกอบปัจจุบันที่ฟองสบู่มาถึงเสมอ และตัวจัดการกำลังทำงานอยู่

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

    โดยที่:

    • this (=event.currentTarget) จะเป็นฟอร์มของตัวเองเสมอ เนื่องจากตัวจัดการถูกทริกเกอร์
    • event.target จะมีลิงก์ไปยังองค์ประกอบเฉพาะภายในแบบฟอร์ม ซึ่งเป็นองค์ประกอบที่ซ้อนกันมากที่สุดที่เกิดการคลิก

    โดยหลักการแล้ว สิ่งนี้อาจเกิดขึ้นพร้อมกับ event.target หากมีการคลิกแบบฟอร์มและไม่มีองค์ประกอบเพิ่มเติมในแบบฟอร์ม

    กำลังหยุดการขึ้น

    โดยทั่วไปแล้ว เหตุการณ์ที่เดือดปุด ๆ จะตรงไปด้านบนและไปถึงออบเจ็กต์หน้าต่างรูท

    แต่สามารถหยุดการขึ้นที่องค์ประกอบตรงกลางได้

    หากต้องการหยุดการแพร่กระจาย คุณต้องเรียกใช้เมธอด event.stopPropagation()

    ลองดูตัวอย่าง: เมื่อคลิกปุ่ม ตัวจัดการ body.onclick จะไม่ทำงาน:

    คลิกฉัน

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

    ดังนั้น stopPropagation จะป้องกันไม่ให้เหตุการณ์แพร่กระจายต่อไป แต่ตัวจัดการทั้งหมดจะทำงานบนองค์ประกอบ แต่ไม่ใช่ในองค์ประกอบถัดไป

    หากต้องการหยุดการประมวลผลองค์ประกอบปัจจุบัน เบราว์เซอร์รองรับเมธอด event.stopImmediatePropagation() วิธีนี้จะไม่เพียงป้องกันการเดือดพล่าน แต่ยังจะหยุดการประมวลผลเหตุการณ์ในองค์ประกอบปัจจุบันด้วย

    ดำน้ำ

    ในมาตรฐานนอกเหนือจากการ "ขึ้น" ของเหตุการณ์แล้วยังมี "การดำน้ำ" อีกด้วย

    การดำน้ำนั้นแตกต่างจากการปีนขึ้นไปตรงที่มีความต้องการน้อยกว่า แต่ก็ยังมีประโยชน์ที่จะรู้เกี่ยวกับเรื่องนี้

    ดังนั้นกิจกรรมจึงมี 3 ขั้นตอน:

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

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

    ข้างต้นฉันเขียนเกี่ยวกับการขึ้นเท่านั้นเนื่องจากขั้นตอนอื่นไม่ได้ใช้และเราไม่มีใครสังเกตเห็น

    ผู้ดูแลไม่ทราบอะไรเกี่ยวกับขั้นตอนการสกัดกั้น แต่เริ่มทำงานจากการขึ้น

    และหากต้องการจับเหตุการณ์ที่ขั้นตอนการสกัดกั้น คุณเพียงแค่ต้องใช้:

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

    ในตัวอย่างที่ , ,

    โปรเซสเซอร์ยังคงเหมือนเดิม แต่คราวนี้อยู่ในขั้นตอนการแช่ หากต้องการดูการทำงานของการสกัดกั้น ให้คลิกที่องค์ประกอบในนั้น

    ตัวจัดการจะทำงานตามลำดับจากบนลงล่าง: FORM → DIV → P

    รหัส JS ที่นี่คือ:

    องค์ประกอบ Var = document.querySelectorAll("form,div,p"); // แนบตัวจัดการกับแต่ละองค์ประกอบที่ขั้นตอนการสกัดกั้นสำหรับ (var i = 0; i< elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); }


    ไม่มีใครหยุดคุณจากการกำหนดตัวจัดการสำหรับทั้งสองขั้นตอน เช่นนี้:

    องค์ประกอบ Var = document.querySelectorAll("form,div,p"); สำหรับ (var i = 0; i< elems.length; i++) { elems[i].addEventListener("click", highlightThis, true); elems[i].addEventListener("click", highlightThis, false); }

    คลิกที่องค์ประกอบภายใน

    หากต้องการดูลำดับเหตุการณ์ ให้ทำดังนี้
    ควรเป็น FORM → DIV → P → P → DIV → FORM โปรดทราบว่าองค์ประกอบ

    จะเข้าร่วมทั้ง 2 ระยะ

    ผลลัพธ์
    • เมื่อมีเหตุการณ์เกิดขึ้น องค์ประกอบที่เกิดเหตุการณ์นั้นจะถูกทำเครื่องหมายเป็น event.target
    • ขั้นแรกเหตุการณ์จะย้ายลงจากรูทเอกสารไปที่ event.target โดยเรียกตัวจัดการไปพร้อมกัน โดยจัดหาผ่าน addEventListener(…., true)
    • เหตุการณ์จะย้ายจาก event.target ไปจนถึงจุดเริ่มต้นของเอกสาร ควบคู่ไปกับการเรียกตัวจัดการที่ระบุผ่าน addEventListener(…., false)

    ตัวจัดการแต่ละคนจะสามารถเข้าถึงคุณสมบัติของเหตุการณ์ได้:

    • event.target เป็นองค์ประกอบที่ลึกที่สุดที่เกิดเหตุการณ์จริง
    • event.currentTarget (=this) – องค์ประกอบที่ตัวจัดการตนเองถูกทริกเกอร์อยู่ในปัจจุบัน (ซึ่งเหตุการณ์ "ถึง")
    • event.eventPhase – ที่เฟสใดที่ตัวจัดการเหตุการณ์ถูกทริกเกอร์ (dive = 1, ascend = 3)

    คุณสามารถหยุดการเผยแพร่ได้โดยการเรียกเมธอด event.stopPropagation() แต่ไม่แนะนำ เนื่องจากคุณอาจต้องการเหตุการณ์เพื่อวัตถุประสงค์ที่ไม่คาดคิดที่สุด