ในอดีต องค์ประกอบ <select> แบบเนทีฟจัดรูปแบบได้ยากและมีได้เฉพาะตัวเลือกข้อความธรรมดาเท่านั้น พร็อพเพอร์ตี้ appearance: base-select มีวิธีประกาศเฉพาะ CSS เพื่อเลือกใช้สถานะที่ปรับแต่งได้สำหรับองค์ประกอบ <select> ซึ่งช่วยให้นักพัฒนาซอฟต์แวร์สามารถรวมเนื้อหา HTML ที่สมบูรณ์ เช่น รูปภาพ, SVG และเลย์เอาต์ที่ซับซ้อน ไว้ภายในองค์ประกอบ <option> ได้ โดยยังคงรักษาการช่วยเหลือพิเศษด้วยแป้นพิมพ์แบบเนทีฟและการผสานรวมแบบฟอร์มไว้ ใช้รูปแบบนี้เพื่อแทนที่คอมโพเนนต์การเลือกที่สร้างขึ้นเองซึ่งมีขนาดใหญ่ด้วยองค์ประกอบมาตรฐานแบบเนทีฟ
วิธีการนำไปใช้งาน
วิธีใช้เครื่องมือเลือกสื่อริชมีเดียโดยใช้ Customizable Select API
- เลือกใช้รูปแบบพื้นฐาน: ใช้
appearance: base-selectกับทั้งองค์ประกอบ<select>และเครื่องมือเลือกภายในโดยใช้องค์ประกอบจำลอง::picker(select)ซึ่งจะเปลี่ยนโปรแกรมแยกวิเคราะห์ HTML ของเบราว์เซอร์สำหรับเนื้อหาภายใน<select> - กำหนดเนื้อหาของปุ่ม: ใช้องค์ประกอบ
<button>และ<selectedcontent>มาตรฐานภายใน<select>เพื่อกำหนดสิ่งที่จะแสดงเมื่อเครื่องมือเลือกปิดอยู่ องค์ประกอบ<selectedcontent>จะแสดงเนื้อหาของตัวเลือกที่เลือกโดยอัตโนมัติ คุณต้องระบุข้อมูลนี้หากต้องการแสดงข้อมูลอย่างละเอียดที่เลือกไว้ในปุ่ม - ใช้ข้อมูลอย่างละเอียดภายในตัวเลือก: ตอนนี้คุณสามารถวางรูปภาพ, SVG และแท็ก HTML อื่นๆ ไว้ภายในแท็ก
<option>ได้แล้ว ก่อนที่จะมี API นี้ เบราว์เซอร์จะตัดแท็กออกจากแท็ก<option>และแสดงผลเฉพาะข้อความธรรมดา - จัดสไตล์ป๊อปโอเวอร์: จัดสไตล์รายการตัวเลือกแบบเลื่อนลงโดยกำหนดเป้าหมายองค์ประกอบจำลอง
::picker(select)โดยจะแสดงผลในเลเยอร์บนสุด ซึ่งหมายความว่าคุณไม่จำเป็นต้องต่อสู้กับz-indexตัวเลือกจะได้รับการจัดตำแหน่งโดยใช้ Anchor Positioning API แบบเนทีฟ
ตัวอย่างโค้ด: เครื่องมือเลือกบทบาทที่สมบูรณ์
<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
<button>
<selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
</button>
<!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
<option value="frontend" aria-label="Frontend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Frontend Developer</span>
<span class="option-desc">React, Vue, CSS</span>
</div>
</option>
<option value="backend" aria-label="Backend Developer">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
</svg>
<div class="option-text">
<span class="option-title">Backend Developer</span>
<span class="option-desc">Node.js, Python</span>
</div>
</option>
</select>
select.custom-select,
select.custom-select::picker(select) {
appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}
select.custom-select {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 12px;
background-color: #1e293b;
border: 1px solid #334155;
border-radius: 8px;
color: #f1f5f9;
cursor: pointer;
}
select.custom-select::picker(select) {
background-color: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
padding: 8px;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}
select.custom-select option {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
border-radius: 4px;
cursor: pointer;
}
select.custom-select option:hover {
background-color: #1e293b;
}
/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
display: none;
}
/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
background-color: #3b82f6;
color: #ffffff;
}
select.custom-select option:checked .option-title {
font-weight: 700;
}
การนำไปใช้เชิงกลยุทธ์และแนวทางปฏิบัติแนะนำ
- ควรใช้
appearance: base-selectเมื่อต้องการเลย์เอาต์ตัวเลือกที่ซับซ้อน (ไอคอน คำอธิบาย รูปภาพ) ซึ่งก่อนหน้านี้ทำได้เฉพาะกับเฟรมเวิร์ก UI ของ JavaScript ที่มีขนาดใหญ่เท่านั้น - อย่าใช้องค์ประกอบเฉพาะกิจหากสังเกตเห็นว่าประสิทธิภาพล่าช้า เนื่องจากเบราว์เซอร์จะจัดการโฟกัสของแป้นพิมพ์แบบเนทีฟอยู่แล้ว
- ควรพิจารณาการแสดงผลเลเยอร์บนสุด เครื่องมือเลือกจะแสดงผลในเลเยอร์บนสุด ซึ่งหมายความว่าจะลบล้าง
z-indexที่เกี่ยวข้องของเนื้อหาในหน้าเว็บ - ควรซ่อนรายละเอียดรอง (เช่น คำอธิบาย) ในสถานะปุ่มหากใช้พื้นที่มากเกินไป โดยจัดรูปแบบ
.custom-select selectedcontent .option-desc { display: none; } - ทดสอบลักษณะการทำงานของเลย์เอาต์ การตั้งค่า
appearance: base-selectจะนำลักษณะการทำงานของเบราว์เซอร์เริ่มต้นในการปรับขนาดการเลือกตามความกว้างของตัวเลือกที่ยาวที่สุดออก คุณอาจต้องตั้งค่าความกว้างคงที่หรือใช้ข้อจำกัด Flex/Grid เพื่อป้องกันไม่ให้เลย์เอาต์เปลี่ยนแปลง - ตรวจสอบว่า
<select>มีแอตทริบิวต์nameและ<label>ที่เชื่อมโยงกัน ซึ่งจะช่วยให้มั่นใจได้ว่าแม้จะมี UI ที่กำหนดเอง คอมโพเนนต์จะยังคงเข้าถึงได้โดยโปรแกรมอ่านหน้าจอและทำงานได้อย่างถูกต้องกับการส่งแบบฟอร์มมาตรฐาน - ใช้แอตทริบิวต์
valueมาตรฐานในตัวเลือกเพื่อให้แน่ใจว่าการส่งแบบฟอร์มทำงานเหมือนเดิมทุกประการ
กลยุทธ์สำรอง
การสนับสนุนเบราว์เซอร์และทางเลือกสำหรับ <select> ที่ปรับแต่งได้
สำหรับเบราว์เซอร์ที่ยังไม่รองรับ appearance: base-select องค์ประกอบ <select> จะลดระดับลงเป็นเมนูแบบเลื่อนลงของระบบปฏิบัติการมาตรฐานอย่างราบรื่น
- ละเว้นเนื้อหาที่ไม่ใช่ข้อความ: เบราว์เซอร์รุ่นเก่าจะตัดแท็ก HTML (เช่น
<svg>หรือ<div>) ภายในแท็ก<option>และแสดงผลเฉพาะโหนดข้อความ ตรวจสอบว่าเนื้อหาข้อความของ<option>อ่านได้และมีความหมายในตัวเอง - การจัดการโครงสร้าง HTML: โปรแกรมแยกวิเคราะห์มาตรฐานอาจละเว้นแท็ก
<button>และ<selectedcontent>ภายใน<select>หรือถือว่าแท็กดังกล่าวไม่ถูกต้อง ไม่จำเป็นต้องใช้ Polyfill ของ JavaScript ที่มีขนาดใหญ่สำหรับการเพิ่มประสิทธิภาพแบบต่อเนื่อง หากคุณมองว่าข้อความมาตรฐานเป็นทางเลือกสำรองที่อ่านได้
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});