เครื่องมือเลือกริชมีเดีย (การเลือกที่ปรับแต่งได้)

ฟีเจอร์บนเว็บ: <select> ที่ปรับแต่งได้

ในอดีต องค์ประกอบ <select> แบบเนทีฟจัดรูปแบบได้ยากและมีได้เฉพาะตัวเลือกข้อความธรรมดาเท่านั้น พร็อพเพอร์ตี้ appearance: base-select มีวิธีประกาศเฉพาะ CSS เพื่อเลือกใช้สถานะที่ปรับแต่งได้สำหรับองค์ประกอบ <select> ซึ่งช่วยให้นักพัฒนาซอฟต์แวร์สามารถรวมเนื้อหา HTML ที่สมบูรณ์ เช่น รูปภาพ, SVG และเลย์เอาต์ที่ซับซ้อน ไว้ภายในองค์ประกอบ <option> ได้ โดยยังคงรักษาการช่วยเหลือพิเศษด้วยแป้นพิมพ์แบบเนทีฟและการผสานรวมแบบฟอร์มไว้ ใช้รูปแบบนี้เพื่อแทนที่คอมโพเนนต์การเลือกที่สร้างขึ้นเองซึ่งมีขนาดใหญ่ด้วยองค์ประกอบมาตรฐานแบบเนทีฟ

วิธีการนำไปใช้งาน

วิธีใช้เครื่องมือเลือกสื่อริชมีเดียโดยใช้ Customizable Select API

  1. เลือกใช้รูปแบบพื้นฐาน: ใช้ appearance: base-select กับทั้งองค์ประกอบ <select> และเครื่องมือเลือกภายในโดยใช้องค์ประกอบจำลอง ::picker(select) ซึ่งจะเปลี่ยนโปรแกรมแยกวิเคราะห์ HTML ของเบราว์เซอร์สำหรับเนื้อหาภายใน <select>
  2. กำหนดเนื้อหาของปุ่ม: ใช้องค์ประกอบ <button> และ <selectedcontent> มาตรฐานภายใน <select> เพื่อกำหนดสิ่งที่จะแสดงเมื่อเครื่องมือเลือกปิดอยู่ องค์ประกอบ <selectedcontent> จะแสดงเนื้อหาของตัวเลือกที่เลือกโดยอัตโนมัติ คุณต้องระบุข้อมูลนี้หากต้องการแสดงข้อมูลอย่างละเอียดที่เลือกไว้ในปุ่ม
  3. ใช้ข้อมูลอย่างละเอียดภายในตัวเลือก: ตอนนี้คุณสามารถวางรูปภาพ, SVG และแท็ก HTML อื่นๆ ไว้ภายในแท็ก <option> ได้แล้ว ก่อนที่จะมี API นี้ เบราว์เซอร์จะตัดแท็กออกจากแท็ก <option> และแสดงผลเฉพาะข้อความธรรมดา
  4. จัดสไตล์ป๊อปโอเวอร์: จัดสไตล์รายการตัวเลือกแบบเลื่อนลงโดยกำหนดเป้าหมายองค์ประกอบจำลอง ::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.
  }
});