การสร้างคอมโพเนนต์กล่องโต้ตอบ

ภาพรวมพื้นฐานของวิธีสร้างโมดัลขนาดเล็กและโมดัลขนาดใหญ่ที่ปรับเปลี่ยนได้ ปรับเปลี่ยนตามอุปกรณ์ และเข้าถึงง่ายด้วยเอลิเมนต์ <dialog>

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

การสาธิตกล่องโต้ตอบขนาดใหญ่และมินิในธีมสว่างและมืด

หากต้องการดูวิดีโอ โปรดใช้โพสต์นี้ในเวอร์ชัน YouTube

ภาพรวม

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

องค์ประกอบ <dialog> มีความเสถียรในเบราว์เซอร์ต่างๆ เมื่อเร็วๆ นี้:

การรองรับเบราว์เซอร์

  • 37
  • 79
  • 98
  • 15.4

แหล่งที่มา

ฉันพบว่าองค์ประกอบหายไปบางอย่าง ดังนั้นใน GUI นี้ ความท้าทาย ฉันเพิ่มประสบการณ์ของนักพัฒนาแอป รายการที่ฉันคาดไว้: เหตุการณ์เพิ่มเติม การปิดไฟ ภาพเคลื่อนไหวที่กำหนดเอง และรูปโปรไฟล์จิ๋ว และประเภทเมกะ

Markup

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

<dialog>
  …
</dialog>

เราสามารถปรับปรุงเกณฑ์พื้นฐานนี้ได้

แต่เดิมนั้น องค์ประกอบกล่องโต้ตอบจะใช้ร่วมกับโมดัลบ่อยๆ และมักใช้ชื่อ แทนกันได้ ฉันมองว่าอิสระในการใช้องค์ประกอบกล่องโต้ตอบสำหรับ ทั้งป๊อปอัปกล่องโต้ตอบขนาดเล็ก (ขนาดเล็ก) และกล่องโต้ตอบแบบเต็มหน้า (ขนาดใหญ่) ฉันตั้งชื่อ ทั้งขนาดใหญ่และขนาดเล็ก โดยกล่องโต้ตอบทั้ง 2 แบบจะมีการปรับให้เหมาะกับกรณีการใช้งานที่แตกต่างกัน เราเพิ่มแอตทริบิวต์ modal-mode แล้วเพื่อให้คุณระบุประเภทต่อไปนี้ได้

<dialog id="MegaDialog" modal-mode="mega"></dialog>
<dialog id="MiniDialog" modal-mode="mini"></dialog>

ภาพหน้าจอของกล่องโต้ตอบทั้งขนาดเล็กและใหญ่ในธีมสว่างและธีมมืด

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

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    …
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

กล่องโต้ตอบ Mega

กล่องโต้ตอบขนาดใหญ่มีองค์ประกอบ 3 อย่างภายในแบบฟอร์มดังนี้ <header> <article>, และ <footer> รูปแบบเหล่านี้จะทำหน้าที่เป็นคอนเทนเนอร์ที่สื่อความหมาย รวมถึงเป้าหมายรูปแบบสำหรับ งานนำเสนอของกล่องโต้ตอบ ส่วนหัวจะระบุโมดัลและเสนอการปิด บทความนี้มีไว้สำหรับอินพุตและข้อมูลฟอร์ม ส่วนท้ายจะมี <menu> จาก ปุ่มดำเนินการ

<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    <header>
      <h3>Dialog title</h3>
      <button onclick="this.closest('dialog').close('close')"></button>
    </header>
    <article>...</article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

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

กล่องโต้ตอบขนาดเล็ก

กล่องโต้ตอบขนาดเล็กคล้ายกับกล่องโต้ตอบขนาดใหญ่มาก จึงเพียงแค่ขาด องค์ประกอบ <header> ซึ่งจะช่วยให้ข้อความมีขนาดเล็กลงและแทรกในบรรทัดมากขึ้น

<dialog id="MiniDialog" modal-mode="mini">
  <form method="dialog">
    <article>
      <p>Are you sure you want to remove this user?</p>
    </article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>

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

การช่วยเหลือพิเศษ

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

กำลังคืนค่าโฟกัส

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

เมื่อใช้องค์ประกอบกล่องโต้ตอบ จะมีลักษณะการทำงานเริ่มต้นในตัวดังนี้

แต่ถ้าคุณต้องการให้กล่องโต้ตอบเคลื่อนไหวไปมา และออก สูญหายไป ในส่วน JavaScript ฉันจะคืนค่า

โฟกัสการดักจับ

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

การรองรับเบราว์เซอร์

  • 102
  • 102
  • 112
  • 15.5

แหล่งที่มา

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

เปิดและโฟกัสองค์ประกอบอัตโนมัติ

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

การปิดด้วยแป้น Escape

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

รูปแบบ

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

การจัดรูปแบบด้วยอุปกรณ์ประกอบแบบเปิด

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

การจัดรูปแบบองค์ประกอบ <dialog>

การเป็นเจ้าของพร็อพเพอร์ตี้การแสดงผล

ลักษณะการทำงานเริ่มต้นในการ "แสดงและซ่อน" ขององค์ประกอบกล่องโต้ตอบจะเปิด/ปิดการแสดงผล พร็อพเพอร์ตี้จาก block ถึง none ขออภัย เนื้อหานี้เป็นภาพเคลื่อนไหวไม่ได้ เข้าและออก แค่เข้า ผมอยากให้ภาพเคลื่อนไหวทั้งขาเข้าและขาออก โดยขั้นตอนแรกคือ เพื่อตั้งค่าของฉันเอง พร็อพเพอร์ตี้ display:

dialog {
  display: grid;
}

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

dialog:not([open]) {
  pointer-events: none;
  opacity: 0;
}

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

กำหนดธีมสีแบบปรับอัตโนมัติให้กล่องโต้ตอบ

กล่องโต้ตอบขนาดใหญ่แสดงธีมสว่างและธีมมืด ซึ่งจะแสดงสีของพื้นผิว

ขณะที่ color-scheme เลือกใช้เอกสารของคุณที่จัดเตรียมโดยเบราว์เซอร์ ธีมสีแบบปรับอัตโนมัติตามค่ากำหนดของระบบสว่างและมืด ฉันต้องการปรับแต่ง องค์ประกอบกล่องโต้ตอบมากขึ้นกว่านั้น Open Props จะมอบพื้นผิวบางส่วน สี ซึ่งจะปรับโดยอัตโนมัติให้เข้ากับ ค่ากำหนดของระบบแบบสว่างและมืดคล้ายกับการใช้ color-scheme เหล่านี้ เหมาะสำหรับการสร้างเลเยอร์ในงานออกแบบ และฉันชอบการใช้สีเพื่อช่วย รองรับลักษณะที่ปรากฏของพื้นผิวเลเยอร์นี้ สีพื้นหลังคือ var(--surface-1); เพื่อวางซ้อนบนเลเยอร์นั้น ให้ใช้ var(--surface-2):

dialog {
  …
  background: var(--surface-2);
  color: var(--text-1);
}

@media (prefers-color-scheme: dark) {
  dialog {
    border-block-start: var(--border-size-1) solid var(--surface-3);
  }
}

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

การปรับขนาดกล่องโต้ตอบที่ปรับเปลี่ยนตามอุปกรณ์

กล่องโต้ตอบมีค่าเริ่มต้นเป็นการมอบสิทธิ์ขนาดให้กับเนื้อหา ซึ่งโดยทั่วไป เยี่ยมเลย เป้าหมายของผมตรงนี้คือ การจำกัด max-inline-size ให้เป็นขนาดที่อ่านได้ (--size-content-3 = 60ch) หรือ 90% ของความกว้างของวิวพอร์ต ช่วงเวลานี้ ทำให้มั่นใจได้ว่าข้อความโต้ตอบบนอุปกรณ์เคลื่อนที่จะไม่ทำให้ข้อความดูไม่สมจริง บนหน้าจอเดสก์ท็อป ทำให้อ่านยาก จากนั้นเพิ่ม max-block-size เพื่อให้กล่องโต้ตอบไม่สูงเกินความสูงของหน้า และยังหมายความว่าเราจะ ต้องระบุตำแหน่งพื้นที่ที่เลื่อนได้ของกล่องโต้ตอบ เผื่อว่าพื้นที่นั้นเป็นแบบสูง กล่องโต้ตอบ

dialog {
  …
  max-inline-size: min(90vw, var(--size-content-3));
  max-block-size: min(80vh, 100%);
  max-block-size: min(80dvb, 100%);
  overflow: hidden;
}

สังเกตไหมว่าฉันมี max-block-size 2 ครั้งแล้ว รายการแรกใช้ 80vh ซึ่งเป็นแท็ก หน่วยวิวพอร์ต สิ่งที่ฉันต้องการจริงๆ คือให้กล่องโต้ตอบ อยู่ในขั้นตอนที่เกี่ยวข้อง สำหรับผู้ใช้ในต่างประเทศ ดังนั้น ผมจะใช้ตรรกะ ใหม่กว่า และเพียงบางส่วนเท่านั้น รองรับ dvb หน่วยในการประกาศครั้งที่ 2 เมื่อมีความเสถียรมากขึ้น

การวางตำแหน่งกล่องโต้ตอบ Mega

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

สไตล์ต่อไปนี้แก้ไของค์ประกอบกล่องโต้ตอบให้เป็นหน้าต่าง โดยยืดออกไปให้แต่ละองค์ประกอบ และใช้ margin: auto เพื่อจัดเนื้อหาให้อยู่กึ่งกลาง:

dialog {
  …
  margin: auto;
  padding: 0;
  position: fixed;
  inset: 0;
  z-index: var(--layer-important);
}
รูปแบบกล่องโต้ตอบขนาดใหญ่สำหรับอุปกรณ์เคลื่อนที่

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

@media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    margin-block-end: 0;
    border-end-end-radius: 0;
    border-end-start-radius: 0;
  }
}

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

การจัดตำแหน่งกล่องโต้ตอบขนาดเล็ก

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

ทำให้โดดเด่น

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

dialog {
  …
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-6);
}

การปรับแต่งองค์ประกอบจำลองฉากหลัง

ฉันเลือกที่จะทำงานกับฉากหลังแบบเบาๆ แค่เพิ่มเอฟเฟ็กต์เบลอด้วย backdrop-filter ไปยังกล่องโต้ตอบขนาดใหญ่ ดังนี้

การรองรับเบราว์เซอร์

  • 76
  • 79
  • 103
  • 18

แหล่งที่มา

dialog[modal-mode="mega"]::backdrop {
  backdrop-filter: blur(25px);
}

ฉันยังเลือกเปลี่ยนไปใช้ backdrop-filter ด้วยเพราะหวังว่าเบราว์เซอร์ต่างๆ จะอนุญาตให้มีการเปลี่ยนองค์ประกอบฉากหลังได้ในอนาคต โดยทำดังนี้

dialog::backdrop {
  transition: backdrop-filter .5s ease;
}

ภาพหน้าจอของกล่องโต้ตอบขนาดใหญ่ที่วางซ้อนบนพื้นหลังแบบเบลอของรูปโปรไฟล์สีสันสดใส

ส่วนเสริมของสไตล์

ฉันเรียกส่วนนี้ว่า "ส่วนเพิ่มเติม" เพราะมันเกี่ยวข้องกับองค์ประกอบ ของกล่องโต้ตอบมากกว่า สาธิตเมื่อเทียบกับองค์ประกอบ ของกล่องโต้ตอบโดยทั่วไป

ปุ่มเลื่อน

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

โดยทั่วไป overscroll-behavior ก็เป็นวิธีแก้ปัญหาตามปกติของฉัน แต่ตาม spec นโยบายนี้ไม่มีผลต่อกล่องโต้ตอบ เพราะไม่ใช่พอร์ตแบบเลื่อน กล่าวคือไม่ใช่ แถบเลื่อน จึงไม่มีอะไรต้องป้องกัน ฉันสามารถใช้ JavaScript เพื่อตรวจสอบ กิจกรรมใหม่จากคู่มือนี้ เช่น "ปิด" และ "เปิด" และสลับ overflow: hidden ในเอกสาร หรือฉันอาจรอให้ :has() คงที่ใน เบราว์เซอร์ทั้งหมด:

การรองรับเบราว์เซอร์

  • 105
  • 105
  • 121
  • 15.4

แหล่งที่มา

html:has(dialog[open][modal-mode="mega"]) {
  overflow: hidden;
}

ขณะนี้เมื่อเปิดกล่องโต้ตอบขนาดใหญ่ เอกสาร HTML จะมี overflow: hidden

เลย์เอาต์ <form>

นอกจากจะเป็นองค์ประกอบที่สำคัญมากในการรวบรวมการโต้ตอบ ข้อมูลจากผู้ใช้นั้นเอง ซึ่งผมนำมาใช้ที่นี่เพื่อจัดวางส่วนหัว ส่วนท้าย และ องค์ประกอบบทความ ในเลย์เอาต์นี้ ผมตั้งใจที่จะสื่อความหมายของบทความย่อยในฐานะ พื้นที่ที่เลื่อนได้ ฉันบรรลุเป้าหมายนี้ได้ด้วย grid-template-rows องค์ประกอบบทความจะได้รับ 1fr และฟอร์มเองมีขีดจำกัดสูงสุดเท่ากัน เป็นองค์ประกอบกล่องโต้ตอบ การตั้งค่าความสูงที่คงที่และขนาดแถวที่คงที่นี้เป็นสิ่งที่ ทำให้องค์ประกอบของบทความถูกจำกัดและเลื่อนเมื่อเกินขีดจำกัด:

dialog > form {
  display: grid;
  grid-template-rows: auto 1fr auto;
  align-items: start;
  max-block-size: 80vh;
  max-block-size: 80dvb;
}

ภาพหน้าจอของเครื่องมือสำหรับนักพัฒนาเว็บที่วางซ้อนข้อมูลเลย์เอาต์ตารางกริดบนแถว

การจัดรูปแบบกล่องโต้ตอบ <header>

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

dialog > form > header {
  display: flex;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  background: var(--surface-2);
  padding-block: var(--size-3);
  padding-inline: var(--size-5);
}

@media (prefers-color-scheme: dark) {
  dialog > form > header {
    background: var(--surface-1);
  }
}

ภาพหน้าจอของ Chrome Devtools ซ้อนทับข้อมูลเลย์เอาต์ Flexbox บนส่วนหัวของกล่องโต้ตอบ

การจัดรูปแบบปุ่มปิดส่วนหัว

เนื่องจากการสาธิตนั้นใช้ปุ่ม Open Props จึงมีการปรับแต่งปุ่มปิด ให้เป็นไอคอนทรงกลมที่มีปุ่มเป็นศูนย์กลาง เช่น

dialog > form > header > button {
  border-radius: var(--radius-round);
  padding: .75ch;
  aspect-ratio: 1;
  flex-shrink: 0;
  place-items: center;
  stroke: currentColor;
  stroke-width: 3px;
}

ภาพหน้าจอของ Chrome Devtools ซ้อนทับข้อมูลขนาดและระยะห่างจากขอบสำหรับปุ่มปิดส่วนหัว

การจัดรูปแบบกล่องโต้ตอบ <article>

องค์ประกอบบทความมีบทบาทพิเศษในกล่องโต้ตอบนี้ โดยเป็นพื้นที่ว่าง ในกรณีของกล่องโต้ตอบที่มีความสูงหรือยาว

เพื่อให้บรรลุเป้าหมายนี้ องค์ประกอบแบบฟอร์มระดับบนสุดได้กำหนดขีดจำกัดสูงสุดสำหรับ ซึ่งมีข้อจำกัดให้องค์ประกอบของบทความนี้สามารถเข้าถึงได้ สูงเกินไป ตั้งค่า overflow-y: auto ให้แสดงแถบเลื่อนเมื่อจำเป็นเท่านั้น ประกอบด้วยการเลื่อนภายในที่มี overscroll-behavior: contain และส่วนที่เหลือ จะเป็นรูปแบบงานนำเสนอที่กำหนดเอง:

dialog > form > article {
  overflow-y: auto; 
  max-block-size: 100%; /* safari */
  overscroll-behavior-y: contain;
  display: grid;
  justify-items: flex-start;
  gap: var(--size-3);
  box-shadow: var(--shadow-2);
  z-index: var(--layer-1);
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: light) {
  dialog > form > article {
    background: var(--surface-1);
  }
}

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

dialog > form > footer {
  background: var(--surface-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  justify-content: space-between;
  align-items: flex-start;
  padding-inline: var(--size-5);
  padding-block: var(--size-3);
}

@media (prefers-color-scheme: dark) {
  dialog > form > footer {
    background: var(--surface-1);
  }
}

ภาพหน้าจอของ Chrome Devtools ซ้อนทับข้อมูลเลย์เอาต์ Flexbox ในองค์ประกอบส่วนท้าย

menu เพื่อรวมปุ่มการทำงานสำหรับกล่องโต้ตอบ มีการใช้การรวม เลย์เอาต์ Flexbox ที่มี gap เพื่อเว้นระยะห่างระหว่างปุ่ม องค์ประกอบเมนู มีระยะห่างจากขอบ เช่น <ul> ผมก็นำสไตล์นั้นออกด้วย เพราะไม่จำเป็นต้องใช้แล้ว

dialog > form > footer > menu {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  padding-inline-start: 0;
}

dialog > form > footer > menu:only-child {
  margin-inline-start: auto;
}

ภาพหน้าจอของ Chrome Devtools ซ้อนทับข้อมูล Flexbox บนองค์ประกอบเมนูส่วนท้าย

แอนิเมชัน

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

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

Open Props มาพร้อมคีย์เฟรมจำนวนมาก ภาพเคลื่อนไหว เป็นกลุ่มที่ง่ายและชัดเจน นี่คือเป้าหมายภาพเคลื่อนไหว และวางซ้อนด้วยเลเยอร์ แนวทางที่ฉันใช้:

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

การเปลี่ยนการใช้งานเริ่มต้นที่ปลอดภัยและมีความหมาย

แม้ว่า Open Props จะมีคีย์เฟรมสำหรับการค่อยๆ เข้าและออก แต่ฉันชอบมากกว่า แนวทางการเปลี่ยนเลเยอร์แบบเลเยอร์เป็นค่าเริ่มต้นโดยมีภาพเคลื่อนไหวของคีย์เฟรม การอัปเกรดที่เป็นไปได้ ก่อนหน้านี้ เราได้กำหนดลักษณะการมองเห็นของกล่องโต้ตอบ ความทึบแสง โดยจัดกลุ่ม 1 หรือ 0 ตามแอตทริบิวต์ [open] ถึง ระหว่าง 0% ถึง 100% ให้บอกเบราว์เซอร์ว่านานแค่ไหนและ การค่อยๆ เปลี่ยนที่ต้องการ

dialog {
  transition: opacity .5s var(--ease-3);
}

การเพิ่มการเคลื่อนไหวให้กับการเปลี่ยน

หากผู้ใช้สามารถเคลื่อนไหวได้ กล่องโต้ตอบขนาดใหญ่และกล่องโต้ตอบขนาดเล็กควรเลื่อน เป็นทางเข้า และขยายขนาดเป็นทางออก คุณสามารถทำได้ด้วย คิวรี่สื่อ prefers-reduced-motion รายการและ Open Props บางรายการ:

@media (prefers-reduced-motion: no-preference) {
  dialog {
    animation: var(--animation-scale-down) forwards;
    animation-timing-function: var(--ease-squish-3);
  }

  dialog[open] {
    animation: var(--animation-slide-in-up) forwards;
  }
}

การปรับภาพเคลื่อนไหวเมื่อออกจากแอปสำหรับอุปกรณ์เคลื่อนที่

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

@media (prefers-reduced-motion: no-preference) and @media (max-width: 768px) {
  dialog[modal-mode="mega"] {
    animation: var(--animation-slide-out-down) forwards;
    animation-timing-function: var(--ease-squish-2);
  }
}

JavaScript

มีหลายสิ่งที่จะเพิ่มด้วย JavaScript:

// dialog.js
export default async function (dialog) {
  // add light dismiss
  // add closing and closed events
  // add opening and opened events
  // add removed event
  // removing loading attribute
}

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

กำลังเพิ่มการปิดไฟ

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

export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
}

const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

ประกาศ dialog.close('dismiss') ระบบจะเรียกเหตุการณ์และระบุสตริง สตริงนี้สามารถเรียกโดย JavaScript อื่น เพื่อรับข้อมูลเชิงลึกเกี่ยวกับวิธี ปิดกล่องโต้ตอบแล้ว คุณจะเห็นสตริงปิดทุกครั้งที่ฉันโทร ฟังก์ชันจากปุ่มต่างๆ เพื่อให้บริบทแก่แอปพลิเคชันของฉันเกี่ยวกับ การโต้ตอบของผู้ใช้

การเพิ่มกิจกรรมปิดและกิจกรรมที่ปิดไปแล้ว

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

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

const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')

export default async function (dialog) {
  …
  dialog.addEventListener('close', dialogClose)
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

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

กำลังเพิ่มเหตุการณ์เปิดและกิจกรรมที่เปิดอยู่

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

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

…
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')

export default async function (dialog) {
  …
  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })
}

const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

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

การเพิ่มกิจกรรมที่นำออก

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

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

…
const dialogRemovedEvent = new Event('removed')

export default async function (dialog) {
  …
  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })
}

const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

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

กำลังนำแอตทริบิวต์การโหลดออก

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

export default async function (dialog) {
  …
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

ดูข้อมูลเพิ่มเติมเกี่ยวกับปัญหาการป้องกันภาพเคลื่อนไหวของคีย์เฟรมในการโหลดหน้าเว็บ ที่นี่

ทั้งหมดรวมกัน

นี่คือ dialog.js ทั้งหมด ตอนนี้เราได้อธิบายแต่ละส่วนแล้ว แยกแต่ละรายการ:

// custom events to be added to <dialog>
const dialogClosingEvent = new Event('closing')
const dialogClosedEvent  = new Event('closed')
const dialogOpeningEvent = new Event('opening')
const dialogOpenedEvent  = new Event('opened')
const dialogRemovedEvent = new Event('removed')

// track opening
const dialogAttrObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(async mutation => {
    if (mutation.attributeName === 'open') {
      const dialog = mutation.target

      const isOpen = dialog.hasAttribute('open')
      if (!isOpen) return

      dialog.removeAttribute('inert')

      // set focus
      const focusTarget = dialog.querySelector('[autofocus]')
      focusTarget
        ? focusTarget.focus()
        : dialog.querySelector('button').focus()

      dialog.dispatchEvent(dialogOpeningEvent)
      await animationsComplete(dialog)
      dialog.dispatchEvent(dialogOpenedEvent)
    }
  })
})

// track deletion
const dialogDeleteObserver = new MutationObserver((mutations, observer) => {
  mutations.forEach(mutation => {
    mutation.removedNodes.forEach(removedNode => {
      if (removedNode.nodeName === 'DIALOG') {
        removedNode.removeEventListener('click', lightDismiss)
        removedNode.removeEventListener('close', dialogClose)
        removedNode.dispatchEvent(dialogRemovedEvent)
      }
    })
  })
})

// wait for all dialog animations to complete their promises
const animationsComplete = element =>
  Promise.allSettled(
    element.getAnimations().map(animation => 
      animation.finished))

// click outside the dialog handler
const lightDismiss = ({target:dialog}) => {
  if (dialog.nodeName === 'DIALOG')
    dialog.close('dismiss')
}

const dialogClose = async ({target:dialog}) => {
  dialog.setAttribute('inert', '')
  dialog.dispatchEvent(dialogClosingEvent)

  await animationsComplete(dialog)

  dialog.dispatchEvent(dialogClosedEvent)
}

// page load dialogs setup
export default async function (dialog) {
  dialog.addEventListener('click', lightDismiss)
  dialog.addEventListener('close', dialogClose)

  dialogAttrObserver.observe(dialog, { 
    attributes: true,
  })

  dialogDeleteObserver.observe(document.body, {
    attributes: false,
    subtree: false,
    childList: true,
  })

  // remove loading attribute
  // prevent page load @keyframes playing
  await animationsComplete(dialog)
  dialog.removeAttribute('loading')
}

การใช้โมดูล dialog.js

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

import GuiDialog from './dialog.js'

const MegaDialog = document.querySelector('#MegaDialog')
const MiniDialog = document.querySelector('#MiniDialog')

GuiDialog(MegaDialog)
GuiDialog(MiniDialog)

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

กำลังฟังเหตุการณ์ใหม่ที่กำหนดเอง

ตอนนี้องค์ประกอบกล่องโต้ตอบที่อัปเกรดแต่ละรายการจะรองรับเหตุการณ์ใหม่ 5 เหตุการณ์ ดังนี้

MegaDialog.addEventListener('closing', dialogClosing)
MegaDialog.addEventListener('closed', dialogClosed)

MegaDialog.addEventListener('opening', dialogOpening)
MegaDialog.addEventListener('opened', dialogOpened)

MegaDialog.addEventListener('removed', dialogRemoved)

ต่อไปนี้เป็นตัวอย่าง 2 ตัวอย่างของการจัดการเหตุการณ์เหล่านั้น

const dialogOpening = ({target:dialog}) => {
  console.log('Dialog opening', dialog)
}

const dialogClosed = ({target:dialog}) => {
  console.log('Dialog closed', dialog)
  console.info('Dialog user action:', dialog.returnValue)

  if (dialog.returnValue === 'confirm') {
    // do stuff with the form values
    const dialogFormData = new FormData(dialog.querySelector('form'))
    console.info('Dialog form data', Object.fromEntries(dialogFormData.entries()))

    // then reset the form
    dialog.querySelector('form')?.reset()
  }
}

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

ประกาศ dialog.returnValue: พารามิเตอร์นี้มีสตริงปิดเมื่อ เรียกเหตุการณ์ close() ของกล่องโต้ตอบ มีความสําคัญอย่างยิ่งในเหตุการณ์ dialogClosed เพื่อ ให้ทราบว่ากล่องโต้ตอบปิด ยกเลิก หรือได้รับการยืนยันหรือยัง หากได้รับการยืนยันแล้ว จากนั้นจะจับค่าของฟอร์มและรีเซ็ตฟอร์ม การรีเซ็ตจะมีประโยชน์ เมื่อกล่องโต้ตอบแสดงขึ้นอีกครั้ง ข้อความนั้นจะว่างเปล่าและพร้อมสำหรับการส่งใหม่

บทสรุป

ตอนนี้คุณก็รู้แล้วว่าฉันทำท่านั้นได้อย่างไร คุณจะทำยังไงบ้างคะ‽ 🙂

มาเพิ่มความหลากหลายให้กับแนวทางของเราและเรียนรู้วิธีทั้งหมดในการสร้างเนื้อหาบนเว็บกัน

สร้างลิงก์สาธิต ทวีตฉัน แล้วฉันจะเพิ่มให้เอง ที่ส่วนรีมิกซ์ของชุมชนด้านล่างนี้ได้เลย

รีมิกซ์ในชุมชน

แหล่งข้อมูล