ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างคอมโพเนนต์แบบเลือกหลายรายการที่ตอบสนอง ปรับขนาดได้ และเข้าถึงได้ เพื่อจัดเรียงและกรองประสบการณ์ของผู้ใช้
ในโพสต์นี้ เราต้องการแชร์แนวคิดเกี่ยวกับวิธีสร้างคอมโพเนนต์แบบเลือกหลายรายการ ลองใช้เดโม
หากต้องการดูวิดีโอ โปรดดูโพสต์เวอร์ชัน YouTube ที่นี่
ภาพรวม
ผู้ใช้มักจะเห็นรายการต่างๆ ซึ่งบางครั้งมีจำนวนมาก และในกรณีเหล่านี้ คุณควรระบุวิธีลดรายการเพื่อไม่ให้ตัวเลือกมากเกินไป บล็อกโพสต์นี้อธิบายการกรอง UI เป็นวิธีลดตัวเลือก โดยแสดงแอตทริบิวต์ของรายการที่ผู้ใช้เลือกหรือยกเลิกการเลือกได้ ซึ่งจะช่วยลดจำนวนผลลัพธ์และทำให้ผู้ใช้มีตัวเลือกไม่มากจนเกินไป
การโต้ตอบ
เป้าหมายคือช่วยให้ผู้ใช้ทุกคนและอินพุตประเภทต่างๆ ของผู้ใช้สามารถไปยังตัวเลือกตัวกรองได้อย่างรวดเร็ว ซึ่งจะแสดงพร้อมกับคอมโพเนนต์คู่ที่ปรับเปลี่ยนและตอบสนองได้ แถบด้านข้างแบบดั้งเดิมของช่องทําเครื่องหมายสําหรับเดสก์ท็อป แป้นพิมพ์ และโปรแกรมอ่านหน้าจอ รวมถึง <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>
เพื่อให้พร็อพเพอร์ตี้ gap
ของ CSS จัดช่องว่างให้เท่าๆ กันและจัดแนวไว้ได้เมื่อป้ายกำกับมีหลายบรรทัด
<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>
การติดตามอินพุตด้วยตัวนับเพื่อแจ้งเทคโนโลยีความช่วยเหลือพิเศษ
เทคนิค status
role ใช้กับประสบการณ์ของผู้ใช้นี้เพื่อติดตามและดูแลรักษาจำนวนตัวกรองสำหรับโปรแกรมอ่านหน้าจอและเทคโนโลยีความช่วยเหลือพิเศษอื่นๆ วิดีโอ 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()
จึงอนุญาตให้เข้าถึงค่าจากเนื้อหาองค์ประกอบเสมือน ดังนี้
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
เพื่อเว้นช่องไฟระหว่าง fieldset
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;
}
ตารางกริดแบบเคลื่อนไหว
Isotope จะสร้างภาพเคลื่อนไหวของเลย์เอาต์ ปลั๊กอินที่มีประสิทธิภาพและทรงพลังสำหรับการจัดเรียงและกรองแบบอินเทอร์แอกทีฟ
JavaScript
นอกจากจะช่วยจัดระเบียบตารางกริดแบบอินเทอร์แอกทีฟที่เคลื่อนไหวอย่างเรียบร้อยแล้ว JavaScript ยังใช้เพื่อขัดเกลาข้อบกพร่องเล็กๆ น้อยๆ ด้วย
การทำให้ค่าที่ผู้ใช้ป้อนเป็นมาตรฐาน
การออกแบบนี้มีแบบฟอร์มเดียวที่มี 2 วิธีในการป้อนข้อมูล และไม่ได้จัดรูปแบบแบบเดียวกัน แต่เราสามารถทำให้เป็นมาตรฐานข้อมูลได้โดยใช้ JavaScript บางรายการ
ฉันเลือกที่จะปรับโครงสร้างข้อมูลองค์ประกอบ <select>
ให้สอดคล้องกับโครงสร้างช่องทําเครื่องหมายแบบกลุ่ม โดยระบบจะเพิ่ม Listener เหตุการณ์ input
ลงในองค์ประกอบ <select>
แล้วแมป selectedOptions
กับ Listener เหตุการณ์นั้น
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 รายการ" เสร็จสมบูรณ์
ตอนนี้ผู้ใช้ทุกคนจะได้รับประสบการณ์การใช้งานเทคโนโลยีความช่วยเหลือพิเศษที่ยอดเยี่ยมไม่ว่าจะโต้ตอบกับเทคโนโลยีดังกล่าวอย่างไรก็ตาม
บทสรุป
ตอนนี้คุณรู้วิธีที่เราทำแล้ว คุณจะทำอย่างไรบ้าง 🙂
มาลองใช้แนวทางที่หลากหลายและดูวิธีทั้งหมดในการสร้างบนเว็บกัน สร้างเดโม แล้วทวีตลิงก์มาหาเรา เราจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง
รีมิกซ์ของชุมชน
ยังไม่มีข้อมูลให้ดู