การสร้างคอมโพเนนต์แบบเลือกหลายรายการ

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

ในโพสต์นี้เราอยากแชร์ความคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์การเลือกหลายรายการ ทดลองใช้ การสาธิต

สาธิต

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

ภาพรวม

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

การโต้ตอบ

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

ภาพหน้าจอเปรียบเทียบที่แสดงเดสก์ท็อปแบบสว่างและมืดพร้อมแถบด้านข้างของ
เทียบกับ iOS บนอุปกรณ์เคลื่อนที่และ Android ที่มีองค์ประกอบแบบเลือกหลายรายการ

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

แตะ

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

ต
ตัวอย่างภาพหน้าจอขององค์ประกอบแบบเลือกหลายรายการใน Chrome บน Android, iPhone และ
iPad iPad และ iPhone มีตัวเลือกตัวเลือกหลายรายการเปิดอยู่ และแต่ละรุ่นจะมี
ประสบการณ์การใช้งานที่ไม่ซ้ำใครซึ่งมีการเพิ่มประสิทธิภาพ
ให้เหมาะกับขนาดหน้าจอ

แป้นพิมพ์และเกมแพด

ด้านล่างนี้เป็นการสาธิตวิธีใช้ <select multiple> จากแป้นพิมพ์

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

Markup

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

<form>

</form>

คอมโพเนนต์ช่องทำเครื่องหมาย

กลุ่มของช่องทำเครื่องหมายควรรวมอยู่ใน <fieldset> และมอบ <legend> เมื่อ HTML มีโครงสร้างแบบนี้ โปรแกรมอ่านหน้าจอและ FormData จะ เข้าใจความสัมพันธ์ขององค์ประกอบต่างๆ โดยอัตโนมัติ

<form>
  <fieldset>
    <legend>New</legend>
    … checkboxes …
  </fieldset>
</form>

เมื่อการจัดกลุ่มเรียบร้อยแล้ว ให้เพิ่ม <label> และ <input type="checkbox"> สำหรับ แต่ละตัวกรอง ฉันเลือกรวมของฉันไว้ใน <div> เพื่อให้พร็อพเพอร์ตี้ CSS gap สามารถจัดระยะห่างอย่างเท่าๆ กันและคงความสอดคล้องเมื่อป้ายกำกับมีบรรทัดหลายบรรทัด

<form>
  <fieldset>
    <legend>New</legend>
    <div>
      <input type="checkbox" id="last 30 days" name="new" value="last 30 days">
      <label for="last 30 days">Last 30 Days</label>
    </div>
    <div>
      <input type="checkbox" id="last 6 months" name="new" value="last 6 months">
      <label for="last 6 months">Last 6 Months</label>
    </div>
   </fieldset>
</form>

ภาพหน้าจอพร้อมการแสดงข้อมูลของคำอธิบายและ
  ของเอลิเมนต์ชุดฟิลด์ จะแสดงสีและชื่อองค์ประกอบ

คอมโพเนนต์ <select multiple>

ฟีเจอร์ที่ไม่ค่อยได้ใช้ขององค์ประกอบ <select> คือ multiple เมื่อใช้แอตทริบิวต์กับองค์ประกอบ <select> ผู้ใช้จะได้รับอนุญาตให้ เลือกหลายรายการจากรายการ คล้ายกับการเปลี่ยนการโต้ตอบจากรายการวิทยุ ไปยังรายการช่องทำเครื่องหมาย

<form>
  <select multiple="true" title="Filter results by category">
    …
  </select>
</form>

หากต้องการติดป้ายกำกับและสร้างกลุ่มภายใน <select> ให้ใช้ <optgroup> แล้วกำหนดแอตทริบิวต์และค่า label องค์ประกอบและแอตทริบิวต์นี้ ค่าคล้ายกับองค์ประกอบ <fieldset> และ <legend>

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      …
    </optgroup>
  </select>
</form>

จากนั้นเพิ่ม <option> สำหรับตัวกรอง

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      <option value="last 30 days">Last 30 Days</option>
      <option value="last 6 months">Last 6 Months</option>
    </optgroup>
  </select>
</form>

ภาพหน้าจอของการแสดงผลบนเดสก์ท็อปขององค์ประกอบที่มีการเลือกหลายรายการ

การติดตามอินพุตด้วยตัวนับเพื่อให้ข้อมูลเทคโนโลยีความช่วยเหลือพิเศษ

สถานะ บทบาท ในประสบการณ์ของผู้ใช้ เพื่อติดตามและคงคะแนน ตัวกรองสำหรับโปรแกรมอ่านหน้าจอและเทคโนโลยีความช่วยเหลืออื่นๆ วิดีโอ YouTube ที่สาธิตฟีเจอร์ การผสานรวมเริ่มต้นด้วย HTML และแอตทริบิวต์ role="status"

<div role="status" class="sr-only" id="applied-filters"></div>

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

aside {
  counter-reset: filters;
}

โดยค่าเริ่มต้น การนับจะเท่ากับ 0 ซึ่งเยี่ยมไปเลย ไม่มีจำนวนอยู่ที่ :checked ตาม ในการออกแบบนี้

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

aside :checked {
  counter-increment: filters;
}

ขณะนี้ CSS รับทราบจำนวนโดยทั่วไปของ UI ช่องทำเครื่องหมายและบทบาทสถานะแล้ว ว่างและกำลังรอค่า เนื่องจาก CSS ยังคงรักษายอดคะแนน ความทรงจำ counter() อนุญาตให้เข้าถึงค่าจาก pseudo เนื้อหา:

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

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

ภาพหน้าจอของโปรแกรมอ่านหน้าจอ MacOS ที่แจ้งจำนวนตัวกรองที่ใช้งานอยู่

ความตื่นเต้นของซ้อนกัน

อัลกอริทึมตัวนับจะทำงานได้เป็นอย่างดีเมื่อใช้ CSS nesting-1 เพราะผมสามารถวาง ให้เป็นบล็อกเดียว พกพาสะดวกและรวมไว้ในที่เดียวสำหรับการอ่านและการอัปเดต

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  & #applied-filters::before {
    content: counter(filters) " filters ";
  }
}

เลย์เอาต์

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

แบบฟอร์ม

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

form {
  display: grid;
  gap: 2ch;
  max-inline-size: 30ch;
}

องค์ประกอบ <select>

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

@media (pointer: coarse) {
  select[multiple] {
    display: block;
  }
}

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

ชุดฟิลด์

การจัดรูปแบบและเลย์เอาต์เริ่มต้นของ <fieldset> ที่มี <legend> จะไม่ซ้ำกัน

ภาพหน้าจอของรูปแบบเริ่มต้นสำหรับชุดฟิลด์และคำอธิบาย

ตามปกติแล้ว ถ้าต้องการเว้นระยะห่างขององค์ประกอบย่อย ฉันจะใช้พร็อพเพอร์ตี้ gap แต่ การวางตำแหน่ง <legend> ทำให้ยากต่อการสร้างชุดที่มีระยะห่างเท่าๆ กัน ของเด็กๆ แทนที่จะเป็น gap, พี่น้องที่อยู่ติดกัน ตัวเลือกและ ใช้ margin-block-start

fieldset {
  padding: 2ch;

  & > div + div {
    margin-block-start: 2ch;
  }
}

วิธีนี้ทำให้ <legend> ไม่ต้องปรับพื้นที่โดยการกำหนดเป้าหมายเฉพาะ เด็ก <div> คน

ภาพหน้าจอแสดงระยะห่างของขอบระหว่างอินพุต แต่ไม่แสดงคำอธิบาย

ป้ายกำกับและช่องทำเครื่องหมายของตัวกรอง

เป็นส่วนย่อยโดยตรงของ <fieldset> และอยู่ภายในความกว้างสูงสุดของฟอร์ม 30ch ข้อความป้ายกำกับอาจตัดข้อความหากยาวเกินไป การตัดข้อความทำได้ดี แต่ ข้อความและช่องทำเครื่องหมายไม่สอดคล้องกัน Flexbox เหมาะกับกรณีนี้

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
ภาพหน้าจอแสดงการจัดเรียงเครื่องหมายถูก
    บรรทัดแรกของข้อความในสถานการณ์การตัดข้อความหลายบรรทัด
เล่นเกมเพิ่มเติมใน Codepen นี้

ตารางกริดแบบเคลื่อนไหว

ภาพเคลื่อนไหวของเลย์เอาต์จะทำโดย Isotope ต ปลั๊กอินที่มีประสิทธิภาพและทรงประสิทธิภาพสำหรับการจัดเรียงและตัวกรองแบบอินเทอร์แอกทีฟ

JavaScript

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

การปรับมาตรฐานอินพุตของผู้ใช้

การออกแบบนี้มีแบบฟอร์มเดียวที่มี 2 วิธีในการป้อนข้อมูล และ สิ่งที่ไม่ควรทำ ทำให้เป็นอนุกรม เดียวกัน แต่ด้วย JavaScript บางส่วน เราสามารถ ทำให้ข้อมูลเป็นมาตรฐานเดียวกัน

ภาพหน้าจอแสดงคอนโซล JavaScript ของเครื่องมือสำหรับนักพัฒนาเว็บ
  จะแสดงเป้าหมายเป็นผลลัพธ์ข้อมูลที่เป็นมาตรฐาน

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

document.querySelector('select').addEventListener('input', event => {
  // make selectedOptions iterable then reduce a new array object
  let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
    // parent optgroup label and option value are added to the reduce aggregator
    data.push([opt.parentElement.label.toLowerCase(), opt.value])
    return data
  }, [])
})

ตอนนี้คุณสามารถส่งแบบฟอร์มได้แล้ว หรือในกรณีที่เป็นการสาธิตนี้ ให้แนะนำ Isotope สิ่งที่จะกรอง

กำลังเสร็จสิ้นองค์ประกอบบทบาทสถานะ

องค์ประกอบจะนับเฉพาะและประกาศจำนวนตัวกรองโดยอิงตามช่องทำเครื่องหมายเท่านั้น การโต้ตอบ แต่ผมคิดว่าเป็นความคิดที่ดีที่จะแชร์จำนวน และตรวจสอบว่ามีการนับตัวเลือกเอลิเมนต์ <select> ด้วย

ตัวเลือกองค์ประกอบ <select> แสดงใน counter()

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

let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length

ผลลัพธ์แสดงในองค์ประกอบ role="status"

:checked มีวิธีส่งตัวกรองที่เลือกจำนวนในตัวไปยัง องค์ประกอบบทบาทสถานะ แต่มองไม่เห็นจำนวนผลลัพธ์ที่กรอง JavaScript สามารถดูการโต้ตอบกับช่องทำเครื่องหมายและหลังจากกรอง ให้เพิ่ม textContent เหมือนกับที่องค์ประกอบ <select> ทำ

document
  .querySelector('aside form')
  .addEventListener('input', e => {
    // isotope demo code
    let filterResults = IsotopeGrid.getFilteredItemElements().length
    document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})

เมื่องานนี้มีประกาศ "ตัวกรอง 2 รายการที่ให้ผลลัพธ์ 25 รายการ"

ภาพหน้าจอของโปรแกรมอ่านหน้าจอ MacOS ที่ประกาศผลลัพธ์

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

บทสรุป

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

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

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

ยังไม่มีอะไรให้ดูที่นี่!