Chrome 135 เพิ่ม `command` และ `commandfor` ให้กับปุ่ม
(developer.chrome.com)- ปุ่มเป็นองค์ประกอบสำคัญในการสร้างเว็บแอปพลิเคชันแบบไดนามิก ใช้สำหรับเปิดเมนู สลับงาน และส่งฟอร์ม
- ใน Chrome 135 มีการเพิ่มแอตทริบิวต์
commandและcommandforใหม่ เพื่อปรับปรุงและแทนที่แอตทริบิวต์เดิมอย่างpopovertargetactionและpopovertarget - ปัญหาที่เกิดขึ้นเมื่อสร้างพฤติกรรมของปุ่มแบบเดิม:
- ตัวจัดการ
onclickของ HTML อาจถูกจำกัดการใช้งานในโค้ดจริงเนื่องจากนโยบายความปลอดภัย (CSP) - ต้องซิงก์สถานะของปุ่มกับองค์ประกอบอื่น และโค้ดสำหรับจัดการสถานะโดยยังคงการเข้าถึงได้ดีนั้นมีความซับซ้อน
- แม้ใน React, AlpineJS, Svelte เป็นต้น การจัดการสถานะและอีเวนต์ก็ยังซับซ้อน
- ตัวจัดการ
แพตเทิร์น command และ commandfor
- เมื่อใช้แอตทริบิวต์
commandและcommandforปุ่มจะสามารถทำงานกับองค์ประกอบอื่นแบบประกาศได้ ช่วยมอบความสะดวกแบบเฟรมเวิร์กพร้อมคงความยืดหยุ่นไว้ - ปุ่ม
commandforใช้ ID (คล้ายแอตทริบิวต์for) และcommandรับค่าที่มีมาให้ในตัว ทำให้แนวทางนี้เข้าใจง่ายขึ้น - ตัวอย่าง: การทำปุ่มเปิดเมนู
- ไม่ต้องใช้
aria-expandedหรือ JavaScript เพิ่มเติม
<button commandfor="my-menu" command="show-popover"> Open Menu </button> <div popover id="my-menu"> <!-- ... --> </div> - ไม่ต้องใช้
command และ commandfor เทียบกับ popovertargetaction และ popovertarget
- หากเคยใช้
popoverมาก่อน คุณอาจคุ้นเคยกับแอตทริบิวต์popovertargetและpopovertargetaction - ทั้งสองทำงานคล้ายกับ
commandforและcommandแต่เจาะจงสำหรับ popover - แอตทริบิวต์ใหม่เข้ามาแทนที่แอตทริบิวต์เดิมทั้งหมด พร้อมเพิ่มความสามารถเพิ่มเติม
คำสั่งในตัว
- แอตทริบิวต์
commandมีการทำงานในตัวที่แมปกับ API หลายตัวshow-popover: แมปกับel.showPopover()hide-popover: แมปกับel.hidePopover()toggle-popover: แมปกับel.togglePopover()show-modal: แมปกับdialogEl.showModal()close: แมปกับdialogEl.close()
- ตัวอย่าง: การทำไดอะล็อกยืนยันการลบ
- จัดการสถานะและการเข้าถึงได้โดยไม่ต้องใช้ JavaScript
<button commandfor="confirm-dialog" command="show-modal"> Delete Record </button> <dialog id="confirm-dialog"> <header> <h1>Delete Record?</h1> <button commandfor="confirm-dialog" command="close" aria-label="Close"> <img role="none" src="/close-icon.svg"> </button> </header> <p>Are you sure? This action cannot be undone</p> <footer> <button commandfor="confirm-dialog" command="close" value="cancel"> Cancel </button> <button commandfor="confirm-dialog" command="close" value="delete"> Delete </button> </footer> </dialog>- โค้ดสำหรับจัดการผลลัพธ์: สามารถจัดการค่าที่ส่งกลับได้ในอีเวนต์
closeของไดอะล็อก
dialog.addEventListener("close", (event) => { if (event.target.returnValue === "cancel") { console.log("Cancel was clicked"); } else if (event.target.returnValue === "delete") { console.log("Delete was clicked"); } });
คำสั่งแบบกำหนดเอง
- นอกจากคำสั่งในตัวแล้ว ยังสามารถกำหนดคำสั่งเองได้ด้วยการใช้คำนำหน้า
-- - คำสั่งแบบกำหนดเองจะทำให้เกิดอีเวนต์
"command"บนองค์ประกอบเป้าหมาย แต่จะไม่ทำตรรกะเพิ่มเติมเอง - ตัวอย่าง: การทำคำสั่งหมุนภาพ
<button commandfor="the-image" command="--rotate-landscape"> Landscape </button> <button commandfor="the-image" command="--rotate-portrait"> Portrait </button> <img id="the-image" src="photo.jpg"> <script type="module"> const image = document.getElementById("the-image"); image.addEventListener("command", (event) => { if (event.command === "--rotate-landscape") { image.style.rotate = "-90deg"; } else if (event.command === "--rotate-portrait") { image.style.rotate = "0deg"; } }); </script>
การจัดการคำสั่งใน Shadow DOM
- ใน Shadow DOM มีข้อจำกัดต่อไปนี้เนื่องจาก
commandforทำงานบนพื้นฐานของ ID:- ไม่สามารถอ้างอิงองค์ประกอบข้าม Shadow DOM ได้
- ในกรณีนี้สามารถใช้ JavaScript API เพื่อตั้งค่าพร็อพเพอร์ตี
.commandForElementได้
- ตัวอย่าง: การเชื่อมคำสั่งใน Shadow DOM
<my-element> <template shadowrootmode="open"> <button command="show-popover">Show popover</button> <slot></slot> </template> <div popover><!-- ... --></div> </my-element> <script> customElements.define("my-element", class extends HTMLElement { connectedCallback() { const popover = this.querySelector('[popover]'); this.shadowRoot.querySelector('button').commandForElement = popover; } }); </script>
แผนในอนาคต
- Chrome มีแผนจะเพิ่มคำสั่งในตัวเพิ่มเติม เช่น:
- การเปิดและปิดองค์ประกอบ
<details> - รองรับคำสั่ง
"show-picker"ใน<input>และ<select> - คำสั่งเล่นสำหรับ
<video>และ<audio> - ความสามารถในการคัดลอกข้อความจากองค์ประกอบ
- การเปิดและปิดองค์ประกอบ
1 ความคิดเห็น
ความเห็นจาก Hacker News
นักทฤษฎีภาษาการเขียนโปรแกรมคาดเดาเกี่ยวกับ "comefrom" ซึ่งเป็นเวอร์ชันที่ทรงพลังกว่าของ "goto" มาตั้งแต่ยุค 80 โดยสิ่งนี้ถูกนำไปใช้จริงเฉพาะใน intercal เท่านั้น แม้ intercal จะเหนือกว่าภาษาอย่าง C ในด้านความปลอดภัย ประสิทธิภาพ และการยศาสตร์ แต่กลับประสบปัญหาในการเข้าสู่ตลาดเชิงพาณิชย์ การได้เห็น javascript นำความสามารถนี้ของ intercal มาใช้เป็นเรื่องน่าสนใจ และหวังว่าสิ่งนี้จะนำไปสู่การเติบโตของการเขียนโปรแกรมแบบสุภาพ เช่นเดียวกับที่อ็อบเจ็กต์แบบ closure ของ javascript เคยผลักดัน functional programming เข้าสู่กระแสหลัก
Invokers ไม่ได้มีเฉพาะใน Chrome เท่านั้น ใน Firefox nightly ก็ใช้งานได้แล้วเช่นกัน
แนวคิดในการทำพฤติกรรม UI แบบประกาศโดยไม่ใช้ JS นั้นน่าสนใจ
aria-expanded)show-modalผสานเรื่องการเข้าถึงเข้ากับมาร์กอัป--rotate-landscape) ทำให้คอมโพเนนต์สามารถเปิดเผย API ผ่าน HTML ได้ข้อสงสัย:
.commandForElementข้าม shadow roots ยังต้องใช้ JS อยู่ดี จึงดูเหมือนเป็นปัญหาที่แก้ไปได้แค่ครึ่งเดียวshow-picker,toggle-details) แพลตฟอร์มจะบวมด้วยไวยากรณ์เฉพาะทางหรือไม่?สเปก:
commandforcommandนี่คือแพตเทิร์น action/messaging ที่ Next, Be, Apple และรายอื่น ๆ ใช้เมื่อราว 30 ปีก่อนหรือเปล่า หรือฉันกำลังพลาดอะไรบางอย่าง?
ชุดเครื่องมือ Java UI ยุคแรกของ Netscape (IFC) ก็เคยทำให้สามารถเชื่อมองค์ประกอบแบบ action เข้าด้วยกันได้
แอตทริบิวต์
commandและcommandforใหม่ ช่วยปรับปรุงและมาแทนที่แอตทริบิวต์popovertargetactionและpopovertargetแพ้ทางอย่างแรงกับการเขียนโปรแกรมผ่านสตริง เข้าใจข้อดีด้าน accessibility แต่ไม่ได้ตื่นเต้นเป็นพิเศษกับการใช้ element ID เป็นอีกชั้นหนึ่งของพฤติกรรมเว็บแอป
ไม่ควรทำสิ่งนี้โดยไม่มี API ที่ครบถ้วน แทนที่จะมีคำสั่งสัก 5 คำสั่ง มันดูเหมือนควรทำให้ความสามารถทั้งหมดของ JavaScript ใช้งานผ่าน HTML ได้ ซึ่งอาจกลายเป็นคำสั่งนับพันรายการ
ตอนแรกนึกว่าจะเป็นเรื่อง command and conquer ใน HTML
การปรับปรุงและขยาย HTML เป็นเรื่องดี แต่ยังต้องไปอีกไกล ทีม HTMX มีไอเดียดี ๆ อยู่บ้าง