Xây dựng thành phần chọn nhiều mục

Thông tin tổng quan cơ bản về cách tạo thành phần có tính thích ứng, khả năng thích ứng và khả năng truy cập nhiều lựa chọn để sắp xếp và lọc trải nghiệm người dùng.

Trong bài đăng này, tôi muốn chia sẻ cách tạo một thành phần chọn nhiều đối tượng. Dùng thử bản minh hoạ.

Bản minh hoạ

Nếu bạn thích xem video hơn, sau đây là phiên bản của bài đăng này trên YouTube:

Tổng quan

Người dùng thường thấy các mục, đôi khi là rất nhiều mục và trong các trường hợp tốt hơn là đưa ra cách giảm danh sách nhằm ngăn chặn quá tải lựa chọn. Chiến dịch này bài đăng trên blog khám phá cách lọc giao diện người dùng như một cách để giảm nhiều lựa chọn. Công cụ này thực hiện việc này bằng cách trình bày các thuộc tính của mặt hàng mà người dùng có thể chọn hoặc bỏ chọn, làm giảm kết quả từ đó giảm tình trạng quá tải lựa chọn.

Lượt tương tác

Mục tiêu là cho phép truyền tải nhanh các tùy chọn bộ lọc cho tất cả người dùng và các loại dữ liệu đầu vào khác nhau. Quảng cáo này sẽ được phân phối cùng với thông báo cặp thành phần. Thanh bên truyền thống của hộp đánh dấu cho màn hình, bàn phím và trình đọc màn hình, cùng với một <select multiple> cho người dùng cảm ứng.

Ảnh chụp màn hình so sánh hiển thị giao diện sáng và tối của màn hình cùng với thanh bên của
hộp đánh dấu so với thiết bị di động iOS và Android có phần tử chọn nhiều mục.

Quyết định sử dụng tính năng chọn nhiều mục tích hợp sẵn cho thao tác chạm, chứ không phải cho máy tính để bàn sẽ giúp tiết kiệm công việc và tạo ra công việc. Tuy nhiên, tôi tin rằng sẽ mang lại trải nghiệm phù hợp mà ít phải tốn mã hơn so với việc xây dựng toàn bộ trải nghiệm phản hồi trong một thành phần.

Cảm ứng

Thành phần cảm ứng lưu trên không gian và giúp người dùng tương tác chính xác hơn thiết bị di động. Tính năng này tiết kiệm không gian bằng cách thu gọn toàn bộ thanh bên của hộp đánh dấu thành một Trải nghiệm chạm lớp phủ tích hợp sẵn của <select>. Công cụ này giúp nhập dữ liệu chính xác bằng cách hiện trải nghiệm lớp phủ cảm ứng lớn do hệ thống cung cấp.

Đáp
bản xem trước ảnh chụp màn hình của phần tử chọn nhiều mục trong Chrome trên Android, iPhone và
iPad. iPad và iPhone đã bật tính năng chọn nhiều đối tượng đang mở và mỗi thiết bị sẽ nhận được một
trải nghiệm độc đáo được tối ưu hoá cho kích thước màn hình.

Bàn phím và tay điều khiển trò chơi

Dưới đây là bản minh hoạ cách sử dụng <select multiple> từ bàn phím.

Tính năng chọn nhiều lựa chọn tích hợp sẵn này không thể tạo kiểu và chỉ được cung cấp trong một thiết bị nhỏ gọn bố cục không phù hợp để hiển thị nhiều tuỳ chọn. Hãy xem cách bạn thực sự không thể bạn thấy vô số lựa chọn trong chiếc hộp nhỏ bé đó? Mặc dù bạn có thể thay đổi kích thước của ảnh, vẫn không hữu dụng như thanh bên của hộp đánh dấu.

Markup (note: đây là tên ứng dụng)

Cả hai thành phần sẽ nằm trong cùng một phần tử <form>. Kết quả của biểu mẫu này, dù là hộp đánh dấu hay chọn nhiều đáp án, sẽ được quan sát và sử dụng để lọc lưới, nhưng cũng có thể được gửi tới máy chủ.

<form>

</form>

Thành phần hộp đánh dấu

Các nhóm hộp đánh dấu nên được gói trong một <fieldset> và cho sẵn một phần tử <legend>. Khi HTML được cấu trúc theo cách này, trình đọc màn hình và FormData sẽ tự động hiểu mối quan hệ của các phần tử.

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

Với nhóm đã sẵn sàng, hãy thêm <label><input type="checkbox"> cho từng bộ lọc. Tôi đã chọn bao bọc trong <div> để thuộc tính CSS gap có thể giãn cách chúng đồng đều và duy trì sự căn chỉnh khi nhãn đi nhiều dòng.

<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>

Ảnh chụp màn hình có lớp phủ thông tin về chú thích và
  các phần tử của nhóm trường, hiển thị màu và tên phần tử.

Thành phần <select multiple>

Một tính năng ít dùng của phần tử <select>multiple. Khi thuộc tính này được sử dụng cùng với phần tử <select>, người dùng được phép hãy chọn nhiều mục trong danh sách. Nó giống như thay đổi lượt tương tác từ một danh sách chọn vào một danh sách hộp đánh dấu.

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

Để gắn nhãn và tạo nhóm bên trong một <select>, hãy sử dụng <optgroup> rồi cung cấp cho phần tử này thuộc tính và giá trị label. Phần tử và thuộc tính này đều giống với phần tử <fieldset><legend>.

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

Giờ thì hãy thêm <option> cho bộ lọc.

<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>

Ảnh chụp màn hình kết xuất một phần tử chọn nhiều mục trên máy tính.

Theo dõi dữ liệu đầu vào bằng bộ đếm để cung cấp thông tin cho công nghệ hỗ trợ

Trạng thái vai trò được áp dụng trong trải nghiệm người dùng này, để theo dõi và duy trì tổng số bộ lọc cho trình đọc màn hình và các công nghệ hỗ trợ khác. Video trên YouTube thể hiện tính năng này. Việc tích hợp bắt đầu với HTML và thuộc tính role="status".

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

Phần tử này sẽ đọc to những thay đổi đối với nội dung. Chúng tôi có thể cập nhật nội dung bằng CSS bộ đếm khi người dùng tương tác với các hộp đánh dấu. Để làm được điều đó, trước tiên, chúng ta cần tạo một bộ đếm có tên trên phần tử mẹ của đầu vào và phần tử trạng thái.

aside {
  counter-reset: filters;
}

Theo mặc định, số lượng sẽ là 0. Rất tốt, không có gì là :checked mặc định trong thiết kế này.

Tiếp theo, để tăng bộ đếm mới tạo, chúng ta sẽ nhắm mục tiêu con của Phần tử <aside> :checked. Khi người dùng thay đổi trạng thái của đầu vào, bộ đếm filters sẽ kiểm đếm.

aside :checked {
  counter-increment: filters;
}

CSS hiện đã biết về kết quả kiểm kê chung của giao diện người dùng hộp đánh dấu và vai trò trạng thái phần tử trống và đang chờ giá trị. Vì CSS đang duy trì số lượng trong bộ nhớ, counter() cho phép truy cập giá trị từ pseudo nội dung của phần tử:

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

HTML cho phần tử vai trò trạng thái giờ đây sẽ thông báo "2 bộ lọc" lên một màn hình người đọc. Đây là một khởi đầu tốt, nhưng chúng ta có thể làm tốt hơn, chẳng hạn như chia sẻ số liệu các kết quả mà bộ lọc đã cập nhật. Chúng tôi sẽ thực hiện công việc này từ JavaScript, ngoài những gì bộ đếm có thể làm.

Ảnh chụp màn hình trình đọc màn hình MacOS cho biết số lượng bộ lọc đang hoạt động.

Lồng ghép sự phấn khích

Thuật toán bộ đếm hoạt động hiệu quả với CSS lồng-1, vì tôi có thể đặt tất cả thành một khối. Tạo cảm giác dễ di chuyển và tập trung để đọc và cập nhật.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

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

Bố cục

Phần này mô tả bố cục giữa 2 thành phần. Hầu hết kiểu bố cục là dành cho thành phần hộp đánh dấu trên màn hình.

Biểu mẫu

Để giúp người dùng dễ đọc và dễ đọc nội dung biểu mẫu hơn, biểu mẫu này được cấp tối đa chiều rộng là 30 ký tự, về cơ bản thiết lập độ rộng đường quang học cho mỗi nhãn bộ lọc. Biểu mẫu này sử dụng bố cục lưới và thuộc tính gap để giãn cách nhóm trường.

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

Phần tử <select>

Danh sách nhãn và hộp đánh dấu đều chiếm quá nhiều không gian trên thiết bị di động. Do đó, bố cục sẽ kiểm tra xem thiết bị trỏ chính của người dùng cần thay đổi trải nghiệm chạm.

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

Giá trị coarse cho biết người dùng sẽ không thể tương tác màn hình với độ chính xác cao với thiết bị đầu vào chính. Trên thiết bị di động, giá trị của con trỏ thường là coarse, là tương tác chính là chạm. Trên thiết bị máy tính, giá trị con trỏ thường là fine vì đây là giá trị phổ biến để kết nối chuột hoặc thiết bị đầu vào có độ chính xác cao khác.

Các nhóm trường

Kiểu và bố cục mặc định của <fieldset><legend> là duy nhất:

Ảnh chụp màn hình các kiểu mặc định cho nhóm trường và chú giải.

Thông thường, để tạo khoảng trống cho các phần tử con, tôi sẽ sử dụng thuộc tính gap, nhưng loại duy nhất vị trí của <legend> khiến khó tạo tập hợp có khoảng cách đồng đều trẻ em. Thay vì gap, thành phần đồng cấp liền kề bộ chọnmargin-block-start được dùng.

fieldset {
  padding: 2ch;

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

Thao tác này bỏ qua việc điều chỉnh không gian của <legend> bằng cách chỉ nhắm mục tiêu <div> trẻ em.

Ảnh chụp màn hình cho thấy khoảng cách lề giữa các giá trị đầu vào nhưng không cho thấy chú giải.

Nhãn và hộp đánh dấu bộ lọc

Là phần tử con trực tiếp của <fieldset> và nằm trong chiều rộng tối đa của biểu mẫu 30ch, văn bản nhãn có thể xuống dòng nếu quá dài. Bao bọc văn bản thật tuyệt, nhưng không khớp giữa văn bản và hộp đánh dấu. Hộp linh hoạt là lựa chọn lý tưởng cho việc này.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
Ảnh chụp màn hình cho thấy cách dấu kiểm căn chỉnh với
    dòng đầu tiên của văn bản trong trường hợp xuống dòng nhiều dòng.
Chơi thêm trong Codepen này

Lưới ảnh động

Ảnh động bố cục do Isotope thực hiện. Đáp hiệu quả và mạnh mẽ cho việc sắp xếp và lọc tương tác.

JavaScript

Ngoài việc giúp sắp xếp một lưới ảnh động gọn gàng, tương tác, JavaScript được dùng để đánh bóng một vài cạnh nhám.

Chuẩn hoá hoạt động đầu vào của người dùng

Thiết kế này có một biểu mẫu với hai cách khác nhau để cung cấp thông tin đầu vào và không nên chuyển đổi tuần tự . Tuy nhiên, với một số JavaScript, chúng ta có thể chuẩn hoá dữ liệu.

Ảnh chụp màn hình bảng điều khiển JavaScript Công cụ cho nhà phát triển
  hiển thị mục tiêu, kết quả dữ liệu được chuẩn hoá.

Tôi đã chọn căn chỉnh cấu trúc dữ liệu của phần tử <select> theo các hộp đánh dấu đã được nhóm cấu trúc. Để làm được điều này, một input trình nghe sự kiện được thêm vào phần tử <select>, lúc đó trình nghe sự kiện selectedOptions được ánh xạ.

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
  }, [])
})

Bây giờ, bạn có thể gửi biểu mẫu một cách an toàn. Trong trường hợp có bản minh hoạ này, hãy hướng dẫn Isotope về những gì để lọc.

Hoàn tất phần tử vai trò trạng thái

Phần tử này chỉ kiểm đếm và thông báo số lượng bộ lọc dựa trên hộp đánh dấu tương tác nhiều hơn, nhưng tôi cho rằng tốt hơn là nên chia sẻ thêm kết quả và đảm bảo các lựa chọn phần tử <select> cũng được tính.

Lựa chọn phần tử <select> được phản ánh trong counter()

Trong mục chuẩn hoá dữ liệu, hệ thống đã tạo một trình nghe khi nhập dữ liệu. Tại số bộ lọc và số kết quả đã chọn ở cuối hàm này cho các bộ lọc đó. Các giá trị có thể được chuyển đến phần tử vai trò trạng thái như thế này.

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

Kết quả được phản ánh trong phần tử role="status"

:checked cung cấp phương thức tích hợp sẵn để truyền số lượng bộ lọc đã chọn tới phần tử vai trò trạng thái, nhưng thiếu khả năng hiển thị số lượng kết quả được lọc. JavaScript có thể theo dõi sự tương tác với các hộp đánh dấu và sau khi lọc lưới, hãy thêm textContent như phần tử <select> đã làm.

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

Tổng cộng, công việc này hoàn tất thông báo "2 bộ lọc cho 25 kết quả".

Ảnh chụp màn hình trình đọc màn hình MacOS đang thông báo kết quả.

Giờ đây, trải nghiệm công nghệ hỗ trợ tuyệt vời của chúng tôi sẽ được cung cấp cho tất cả người dùng theo cách họ tương tác với ứng dụng đó.

Kết luận

Giờ bạn đã biết cách tôi thực hiện điều đó, bạn sẽ làm cách nào‽ 🙂

Hãy đa dạng hoá phương pháp tiếp cận và tìm hiểu tất cả các cách xây dựng ứng dụng trên web. Tạo một bản minh hoạ, tweet cho tôi các đường liên kết và tôi sẽ thêm vào vào phần bản phối lại của cộng đồng dưới đây!

Bản phối lại của cộng đồng

Chưa có gì để xem ở đây!