ภาพรวมพื้นฐานของวิธีสร้างคอมโพเนนต์แบบเลือกหลายรายการที่ปรับเปลี่ยนตามอุปกรณ์ ปรับเปลี่ยนได้ และเข้าถึงง่ายสำหรับจัดเรียงและกรองประสบการณ์ของผู้ใช้
ในโพสต์นี้เราอยากแชร์ความคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์การเลือกหลายรายการ ทดลองใช้ การสาธิต
หากต้องการดูวิดีโอ โปรดใช้โพสต์นี้ในเวอร์ชัน YouTube
ภาพรวม
ผู้ใช้มักจะเห็นสินค้า บางครั้งมีจำนวนมาก และใน ก็ควรหาวิธีลดรายการเพื่อป้องกัน มีการเลือกมากเกินไป ช่วงเวลานี้ บล็อกโพสต์สำรวจ UI การกรองเพื่อช่วยลดตัวเลือก ดำเนินการโดย การนำเสนอแอตทริบิวต์รายการที่ผู้ใช้สามารถเลือกหรือยกเลิกการเลือก ทำให้ได้ผลลัพธ์น้อยลง จึงเป็นการลดภาระของตัวเลือกลง
การโต้ตอบ
เป้าหมายคือการทำให้ตัวเลือกการกรองข้ามผ่านได้อย่างรวดเร็วสำหรับผู้ใช้ทั้งหมดและผู้ใช้
อินพุตประเภทต่างๆ ซึ่งจะส่งมาพร้อมกับอุปกรณ์
ที่ปรับเปลี่ยนได้และปรับเปลี่ยนตามอุปกรณ์
2 คอมโพเนนต์ แถบด้านข้างของช่องทำเครื่องหมายสำหรับเดสก์ท็อปและแป้นพิมพ์
และโปรแกรมอ่านหน้าจอ และ <select
multiple>
สำหรับผู้ใช้ระบบสัมผัส
การตัดสินใจนี้ใช้การเลือกหลายรายการในตัวสำหรับการแตะที่มีในตัว (ไม่ใช่ในเดสก์ท็อป) ช่วยประหยัดงานและสร้างสรรค์ผลงาน แต่ผมเชื่อว่าจะให้ประสบการณ์ที่เหมาะสมโดยมีหนี้สินจากโค้ดน้อยกว่าการสร้างประสบการณ์ที่ปรับเปลี่ยนตามอุปกรณ์ทั้งหมดในคอมโพเนนต์เดียว
แตะ
คอมโพเนนต์การสัมผัสจะช่วยประหยัดพื้นที่และช่วยให้การโต้ตอบของผู้ใช้มีความแม่นยำ
มือถือ ซึ่งช่วยประหยัดพื้นที่โดยการยุบแถบด้านข้างของช่องทำเครื่องหมายทั้งหมดเป็น
ประสบการณ์การสัมผัสแบบซ้อนทับในตัว <select>
ช่วยเพิ่มความถูกต้องในการป้อนข้อมูลด้วยการแสดง
การแสดงผลด้วยการสัมผัสขนาดใหญ่ซึ่งระบบมีให้
แป้นพิมพ์และเกมแพด
ด้านล่างนี้เป็นการสาธิตวิธีใช้ <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 ของสิ่งที่ตัวนับสามารถทำได้
ความตื่นเต้นของซ้อนกัน
อัลกอริทึมตัวนับจะทำงานได้เป็นอย่างดีเมื่อใช้ 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;
}
![ภาพหน้าจอแสดงการจัดเรียงเครื่องหมายถูก
บรรทัดแรกของข้อความในสถานการณ์การตัดข้อความหลายบรรทัด](https://web.dev/static/articles/building/a-multi-select-component/image/screenshot-showing-the-c-b135285ef7d8a.png?authuser=1&hl=th)
ตารางกริดแบบเคลื่อนไหว
ภาพเคลื่อนไหวของเลย์เอาต์จะทำโดย Isotope ต ปลั๊กอินที่มีประสิทธิภาพและทรงประสิทธิภาพสำหรับการจัดเรียงและตัวกรองแบบอินเทอร์แอกทีฟ
JavaScript
นอกจากจะช่วยจัดตาราง JavaScript แบบอินเทอร์แอกทีฟที่เคลื่อนไหวและโต้ตอบได้ดีแล้ว จะใช้ขัดขอบที่หยาบเล็กน้อย
การปรับมาตรฐานอินพุตของผู้ใช้
การออกแบบนี้มีแบบฟอร์มเดียวที่มี 2 วิธีในการป้อนข้อมูล และ สิ่งที่ไม่ควรทำ ทำให้เป็นอนุกรม เดียวกัน แต่ด้วย 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 รายการ"
ตอนนี้ ประสบการณ์เทคโนโลยีความช่วยเหลือพิเศษของเราจะส่งไปถึง แต่ผู้ใช้โต้ตอบกับผลิตภัณฑ์อย่างไร
บทสรุป
ตอนนี้คุณก็รู้แล้วว่าฉันทำท่านั้นได้อย่างไร คุณจะทำยังไงบ้างคะ‽ 🙂
มาเพิ่มความหลากหลายให้กับแนวทางของเราและเรียนรู้วิธีทั้งหมดในการสร้างเนื้อหาบนเว็บกัน สร้างลิงก์สาธิต ทวีตฉัน แล้วฉันจะเพิ่มให้เอง ที่ส่วนรีมิกซ์ของชุมชนด้านล่างนี้ได้เลย
รีมิกซ์ในชุมชน
ยังไม่มีอะไรให้ดูที่นี่!