Xây dựng thành phần breadcrumb (tập hợp liên kết phân cấp)

Tổng quan cơ bản về cách tạo thành phần đường dẫn thích ứng và dễ truy cập để người dùng điều hướng trang web của bạn.

Trong bài đăng này, tôi muốn chia sẻ cách tạo thành phần breadcrumb (tập hợp liên kết phân cấp). Xem 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

Thành phần breadcrumb (tập hợp liên kết phân cấp) hiển thị người dùng ở đâu trong hệ thống phân cấp trang web. Tên này đến từ Hansel và Gretel, đã ra mắt những vụn đường mòn phía sau họ trong một vài khu rừng tối và họ tìm được đường về nhà bằng cách truy vết ngược trở lại.

Đường dẫn trong bài đăng này không chuẩn breadcrumb (tập hợp liên kết phân cấp), chúng giống như đường dẫn. Chúng cung cấp chức năng bổ sung bằng cách đặt các thuộc tính đồng cấp vào ngay phần điều hướng bằng <select>, cho phép truy cập nhiều lớp nhất có thể.

Trải nghiệm người dùng ở chế độ nền

Trong video minh hoạ thành phần ở trên, các danh mục phần giữ chỗ là thể loại trò chơi điện tử. Đường nhỏ này được tạo bằng cách điều hướng theo đường dẫn sau: home » rpg » indie » on sale, như minh hoạ dưới đây.

Thành phần breadcrumb (tập hợp liên kết phân cấp) này sẽ cho phép người dùng di chuyển qua lại phân cấp thông tin; nhảy cành cây và chọn trang nhanh chóng và sự chính xác.

Cấu trúc thông tin

Tôi thấy hữu ích khi suy nghĩ về bộ sưu tập và các mục.

Bộ sưu tập

Tập hợp là một loạt các tuỳ chọn để lựa chọn. Từ trang chủ của nguyên mẫu đường dẫn của bài đăng này, các bộ sưu tập là FPS, RPG, brawler, trình thu thập thông tin trong hầm ngục, thể thao và giải đố.

Mục

Trò chơi điện tử là một mặt hàng, một bộ sưu tập cụ thể cũng có thể là một mặt hàng nếu đại diện cho một bộ sưu tập khác. Ví dụ: RPG là một mặt hàng và bộ sưu tập. Khi nội dung là một mục, người dùng sẽ ở trên trang bộ sưu tập đó. Ví dụ: chúng trên trang RPG (trò chơi nhập vai), hiển thị danh sách các trò chơi RPG, bao gồm các danh mục con bổ sung AAA, Indie và Tự xuất bản.

Theo khoa học máy tính, thành phần breadcrumb (tập hợp liên kết phân cấp) này biểu thị đa chiều mảng:

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

Ứng dụng hoặc trang web của bạn sẽ có cấu trúc thông tin tuỳ chỉnh (IA) tạo ra một mảng đa chiều khác nhau, nhưng tôi hy vọng khái niệm đích thu thập truyền tải qua hệ thống phân cấp và trang cũng có thể vào đường dẫn của bạn.

Bố cục

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

Các thành phần tốt bắt đầu bằng HTML thích hợp. Trong phần tiếp theo này, tôi sẽ đề cập đến các lựa chọn đánh dấu và cách chúng tác động đến thành phần tổng thể.

Bảng phối màu tối và sáng

<meta name="color-scheme" content="dark light">

Thẻ meta color-scheme trong phần trên đoạn mã thông báo cho trình duyệt rằng trang này muốn trình duyệt sáng và tối kiểu. Đường dẫn ví dụ không bao gồm bất kỳ CSS nào cho các bảng phối màu này, và do đó breadcrumb (tập hợp liên kết phân cấp) sẽ sử dụng các màu mặc định do trình duyệt cung cấp.

<nav class="breadcrumbs" role="navigation"></nav>

Bạn nên sử dụng Phần tử <nav> đối với điều hướng trang web, có vai trò ARIA ngầm ẩn là chỉ đường. Trong khi kiểm thử, tôi nhận thấy rằng việc có thuộc tính role đã thay đổi cách trình đọc màn hình tương tác với phần tử này, phần tử này thực sự được thông báo là điều hướng và do đó tôi đã chọn thêm nó.

Biểu tượng

Khi một biểu tượng lặp lại trên một trang, SVG (Đồ hoạ vectơ có thể mở rộng) Phần tử <use> có nghĩa là bạn có thể xác định path một lần và sử dụng nó cho tất cả các bản sao của . Điều này giúp thông tin đường dẫn không bị lặp lại, khiến các tài liệu lớn hơn và có thể dẫn đến việc đường dẫn không nhất quán.

Để sử dụng kỹ thuật này, hãy thêm một phần tử SVG bị ẩn vào trang và gói các biểu tượng trong phần tử <symbol> có một mã nhận dạng duy nhất:

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

Trình duyệt đọc HTML của SVG, đưa thông tin biểu tượng vào bộ nhớ và tiếp tục với phần còn lại của trang tham chiếu đến ID để sử dụng thêm biểu tượng, như sau:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

Công cụ cho nhà phát triển cho thấy phần tử sử dụng SVG được kết xuất.

Xác định một lần, sử dụng bao nhiêu lần tuỳ thích mà giảm thiểu tác động đến hiệu suất trang và tạo kiểu linh hoạt. Lưu ý rằng aria-hidden="true" đã được thêm vào phần tử SVG. Các biểu tượng này không hữu ích cho người duyệt xem nội dung chỉ nghe được nội dung và đang ẩn khỏi những người dùng đó sẽ ngăn họ thêm nhiễu không cần thiết.

Đây là nơi breadcrumb (tập hợp liên kết phân cấp) truyền thống và breadcrumb (tập hợp liên kết phân cấp) trong thành phần này khác nhau. Thông thường, đây chỉ là một đường liên kết <a>, nhưng tôi đã thêm trải nghiệm người dùng truyền tải bằng một lựa chọn nguỵ trang. Lớp .crumb chịu trách nhiệm bố trí đường liên kết và biểu tượng, trong khi .crumbicon chịu trách nhiệm xếp chồng biểu tượng và chọn với nhau. Tôi gọi nó là liên kết phân tách vì các chức năng của nó rất tương tự như một nút chia tách nhưng còn để điều hướng trang.

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

Đường liên kết và một số tuỳ chọn không có gì đặc biệt nhưng bổ sung thêm nhiều chức năng cho breadcrumb (tập hợp liên kết phân cấp) đơn giản. Việc thêm title vào phần tử <select> sẽ giúp ích cho màn hình người dùng đọc, cung cấp cho họ thông tin về hành động của nút. Tuy nhiên cũng trợ giúp như nhau cho những người khác, bạn sẽ thấy công cụ đó ở ngay trước mắt iPad. Một thuộc tính cung cấp bối cảnh của nút cho nhiều người dùng.

Ảnh chụp màn hình cho thấy phần tử lựa chọn ẩn đang được di chuột và phần tử
đang hiển thị chú thích theo ngữ cảnh.

Trang trí dòng phân cách

<span class="crumb-separator" aria-hidden="true">→</span>

Dòng phân cách là không bắt buộc, bạn chỉ cần thêm một dòng phân cách cũng rất hiệu quả (xem ví dụ thứ ba trong video ở trên). Sau đó, tôi tặng từng aria-hidden="true" vì chúng chỉ mang tính chất trang trí chứ không phải để trang trí điều gì đó mà trình đọc màn hình cần thông báo.

Thuộc tính gap được đề cập tiếp theo giúp cho khoảng cách của các thuộc tính này trở nên đơn giản.

Kiểu

Vì màu này sử dụng màu hệ thống, nên chủ yếu là các khoảng trống và ngăn xếp cho kiểu!

Hướng và luồng bố cục

Công cụ cho nhà phát triển cho thấy cách căn chỉnh đường dẫn điều hướng với lớp phủ hộp linh hoạt của công cụ
của chúng tôi.

Phần tử điều hướng chính nav.breadcrumbs đặt một thuộc tính tuỳ chỉnh có phạm vi để con sử dụng, và nếu không thì thiết lập căn chỉnh ngang theo chiều dọc của bạn. Điều này sẽ đảm bảo vụn bánh, đường chia và biểu tượng căn chỉnh.

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

Một breadcrumb (tập hợp liên kết phân cấp) hiển thị theo chiều dọc với lớp phủ Flexbox.

Mỗi .crumb cũng thiết lập một bố cục được căn chỉnh theo chiều ngang với một số khoảng trống, nhưng đặc biệt nhắm mục tiêu đến các thành phần con liên kết con và xác định kiểu white-space: nowrap Điều này rất quan trọng đối với đường dẫn nhiều từ vì chúng tôi đều muốn phân phối theo nhiều dòng. Ở phần sau của bài đăng này, chúng ta sẽ thêm các kiểu để xử lý tràn lề theo chiều ngang mà thuộc tính white-space này gây ra.

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

aria-current="page" được thêm để giúp liên kết trang hiện tại nổi bật so với nghỉ ngơi. Người dùng trình đọc màn hình không chỉ có chỉ báo rõ ràng rằng đường liên kết cho trang hiện tại, chúng tôi đã tạo kiểu trực quan cho phần tử để giúp người dùng có được trải nghiệm người dùng tương tự.

Thành phần .crumbicon sử dụng lưới để xếp chồng biểu tượng SVG có phần tử "gần như không hiển thị" Phần tử <select>.

Công cụ cho nhà phát triển lưới hiển thị lớp phủ một nút mà trong đó cả hàng và cột đều nằm
ngăn xếp được đặt tên.

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

Phần tử <select> nằm cuối cùng trong DOM, vì vậy, phần tử này nằm ở đầu ngăn xếp, và giàu tính tương tác. Thêm kiểu opacity: .01 để phần tử vẫn sử dụng được, kết quả là một hộp chọn hoàn toàn vừa vặn với hình dạng của biểu tượng. Đây là một cách hay để tuỳ chỉnh giao diện của phần tử <select> trong khi duy trì chức năng tích hợp sẵn.

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

Trình đơn mục bổ sung

Breadcrumb phải có khả năng biểu thị một đường mòn rất dài. Tôi thích việc cho phép để mọi thứ bị lệch ra khỏi màn hình theo chiều ngang khi thích hợp và tôi cảm thấy điều này breadcrumb (tập hợp liên kết phân cấp) đủ điều kiện.

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

Các kiểu mục bổ sung thiết lập trải nghiệm người dùng sau:

  • Cuộn theo chiều ngang có vùng chứa cuộn quá mức.
  • Khoảng đệm cuộn theo chiều ngang.
  • Một điểm nhanh trên vụn cuối cùng. Điều này có nghĩa là trên trang, hãy tải tải vụn được chụp nhanh và trong tầm nhìn.
  • Xóa điểm chụp nhanh khỏi Safari, điểm này gặp khó khăn với chiều ngang kết hợp hiệu ứng cuộn và chụp nhanh.

Truy vấn về nội dung nghe nhìn

Một điều chỉnh tinh tế cho khung nhìn nhỏ hơn là ẩn "Trang chủ" nhãn, đang rời khỏi chỉ biểu tượng:

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

Cạnh nhau của breadcrumb (tập hợp liên kết phân cấp) có và không có nhãn nhà, cho
so sánh.

Hỗ trợ tiếp cận

Có chuyển động

Không có nhiều chuyển động trong thành phần này nhưng bằng cách gói hiệu ứng chuyển đổi trong quá trình kiểm tra prefers-reduced-motion, chúng ta có thể ngăn chặn chuyển động không mong muốn.

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

Không có kiểu nào khác cần thay đổi, các hiệu ứng di chuột và lấy nét thật tuyệt vời và có ý nghĩa nếu không có transition, nhưng nếu chuyển động được chấp nhận, chúng ta sẽ thêm sang tương tác.

JavaScript

Trước tiên, bất kể bạn dùng loại bộ định tuyến nào trong trang web hoặc ứng dụng của mình, khi người dùng thay đổi đường dẫn, URL cần được cập nhật và người dùng hiển thị trang thích hợp. Thứ hai, để chuẩn hoá trải nghiệm người dùng, hãy đảm bảo không có thao tác điều hướng ngoài dự kiến nào xảy ra khi người dùng chỉ duyệt xem <select> .

Hai phương pháp đo lường trải nghiệm người dùng quan trọng sẽ được JavaScript xử lý: chọn đã đã thay đổi và sẵn sàng <select> thay đổi ngăn chặn kích hoạt sự kiện.

Cần tính năng ngăn chặn sự kiện sớm do việc sử dụng <select> . Trên Windows Edge và có thể cũng như các trình duyệt khác, hãy chọn changed sự kiện sẽ kích hoạt khi người dùng duyệt qua các lựa chọn bằng bàn phím. Đây là lý do tôi gọi là háo hức, vì người dùng chỉ giả chọn một tuỳ chọn, chẳng hạn như di chuột hoặc tiêu điểm nhưng chưa xác nhận lựa chọn bằng enter hay click. Người háo hức sự kiện làm cho tính năng thay đổi danh mục thành phần này không thể truy cập được, bởi vì mở hộp chọn và chỉ cần duyệt qua mục sẽ kích hoạt sự kiện và hãy thay đổi trang trước khi người dùng sẵn sàng.

Một sự kiện đã thay đổi <select> tốt hơn

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

Chiến lược cho việc này là theo dõi các sự kiện nhấn phím tắt trên mỗi <select> và xác định xem phím được nhấn có phải là do xác nhận điều hướng hay không (Tab hoặc Enter) hoặc tính năng điều hướng theo không gian (ArrowUp hoặc ArrowDown). Bằng cách này thành phần có thể quyết định đợi hay tiếp tục, khi sự kiện cho Phần tử <select> kích hoạt.

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