Перейти к содержимому

breadcrumb хлебные крошки —— Навигация

Хлебные крошки — это индикатор пути «где я и как я сюда попал»: Главная / Настройки / Безопасность / Двухфакторная аутентификация. Они легче меню — передают только иерархический путь, не неся основную навигацию; слабее индикатора шагов — не выражают последовательность процесса, а выражают отношение вложенности. Когда уровней много, путь становится длинным, и сворачивание — его основной алгоритм.

  • Реализация <LhBreadcrumb> / <LhBreadcrumbItem> на всех концах: иерархические пути в административных системах, пути категорий на сайтах документации, категории товаров.
  • Сценарии, где нужно показать текущую позицию и предоставить вход для постепенного возврата.
  • Не следует использовать: основная навигация (используйте menu); пошаговые процессы (используйте steps); переключение однорангового содержимого (используйте tabs); путь из одного уровня (это не хлебные крошки, а заголовок страницы).
import {
getBreadcrumbItemClassNames,
getEllipsisBeforeIndex,
getCollapsedHiddenIndexes,
getBreadcrumbItemAria,
} from '@lh-ui/share/core/breadcrumb'
import type { LhBreadcrumbItemState, LhBreadcrumbCollapseState } from '@lh-ui/share/core/breadcrumb'

При сворачивании сохраняется первый элемент: правило getEllipsisBeforeIndex — «первый элемент + многоточие + последние maxItems-1 элементов». Первый элемент сохраняется всегда — это якорь «вернуться на самый верхний уровень» (обычно «Главная»), и его скрытие лишит пользователя возможности вернуться к корню. Этот компромисс так же важен, как и «сохранение последнего элемента» (текущая позиция).

Перед каким элементом вставляется многоточие: возвращается индекс первого видимого элемента после многоточия (total - (maxItems - 1)); UI-слой вставляет элемент-многоточие перед этой позицией. Без сворачивания возвращается -1 — обратите внимание: -1 это сигнальное значение «без сворачивания», а не валидный индекс.

При maxItems < 2 сворачивания нет: нужно показать хотя бы первый элемент + один последующий, иначе сворачивание лишено смысла. При maxItems: 1 и меньше возвращается -1 — это защитное проектирование, чтобы при неверной настройке хлебные крошки не свернулись в одно только многоточие.

Связь getCollapsedHiddenIndexes с индексом многоточия: скрываются элементы «между первым элементом и многоточием», то есть полуоткрытый интервал [1, ellipsisIndex) — первый элемент (индекс 0) и элементы после позиции многоточия сохраняются. Функции работают в паре: сначала берётся позиция многоточия, затем вычисляется множество скрытых.

aria-current=“page” у последнего элемента: последний элемент — «текущая страница», и aria-current="page" — стандартная семантика WAI-ARIA; скринридер озвучивает «текущая страница», что точнее текстовой подсказки. Для не последних элементов aria-атрибуты не выводятся (семантика ссылки несёт нативный <a>).

Функция Сигнатура Возврат Назначение
getBreadcrumbItemClassNames (state: LhBreadcrumbItemState) => string[] классы состояние ссылки + последний элемент
getEllipsisBeforeIndex (state: LhBreadcrumbCollapseState) => number индекс / -1 позиция многоточия
getCollapsedHiddenIndexes (state: LhBreadcrumbCollapseState) => number[] множество скрытых индексов скрытые элементы при сворачивании
getBreadcrumbItemAria (isLast: boolean) => LhAriaAttrs aria-пакет aria-current для последнего элемента
export interface LhBreadcrumbItemState {
isLast: boolean // последний ли элемент (текущая страница)
isLink: boolean // рендерится ли как ссылка (передан to и не последний)
}
export interface LhBreadcrumbCollapseState {
total: number // общее количество элементов
maxItems: number // максимальное число видимых элементов (включая многоточие); меньше 2 — без сворачивания
}
getBreadcrumbItemAria(true) // { 'aria-current': 'page' }
getBreadcrumbItemAria(false) // {}
  • aria-current=“page” только у последнего элемента: промежуточные элементы — кликабельные ссылки, им не нужна текущая метка. Скринридер при входе на страницу озвучит: «Главная, ссылка / Настройки, ссылка / Безопасность, ссылка / Двухфакторная аутентификация, текущая страница» — путь и позиция передаются целиком за один раз.
  • Нативный <a> вместо перехода через JS: если промежуточный элемент — реальная ссылка, используйте <a href="..."> — поддерживается открытие в новой вкладке средней кнопкой, копирование ссылки правой кнопкой, работа без JS. Клиентский роутинг реализуется перехватом клика в UI-слое (@click.prevent), href сохраняется. Комментарий к getBreadcrumbItemAria объясняет это: при наличии ссылки роль берёт на себя нативный a, функции выводить ничего не нужно.
  • Доступность кнопки-многоточия: свёрнутое многоточие обычно кликабельно и разворачивает полный путь. Это элемент управления, ему нужен aria-label="Развернуть полный путь" (либо aria-expanded для выражения состояния развёрнутости) — иначе это безымянная кнопка.
  • Обёртка nav + aria-label: хлебным крошкам рекомендуется <nav aria-label="Хлебные крошки"> — на странице может быть несколько nav (основная навигация, постраничная), aria-label их различает.
  • Разделители декоративны: разделители / или » — чистая декорация: aria-hidden или реализация через CSS-псевдоэлемент, чтобы скринридер не зачитывал каждый слеш.
import {
getEllipsisBeforeIndex, getCollapsedHiddenIndexes, getBreadcrumbItemAria,
} from '@lh-ui/share/core/breadcrumb'
getEllipsisBeforeIndex({ total: 6, maxItems: 4 }) // 3 (многоточие вставляется перед индексом 3)
getEllipsisBeforeIndex({ total: 4, maxItems: 4 }) // -1 (всего <= maxItems, без сворачивания)
getEllipsisBeforeIndex({ total: 6, maxItems: 1 }) // -1 (maxItems < 2, без сворачивания)
getEllipsisBeforeIndex({ total: 6, maxItems: 2 }) // 5 (первый элемент + последние 1)
getCollapsedHiddenIndexes({ total: 6, maxItems: 4 }) // [1, 2] (между первым элементом и многоточием)
getCollapsedHiddenIndexes({ total: 4, maxItems: 4 }) // [] (без сворачивания)
// UI-слой: свёрнутый рендер (состояние развёрнутости управляется локальным state)
const expanded = ref(false)
const items = ['Главная', 'Система', 'Настройки', 'Безопасность', 'Аккаунт', 'Двухфакторная аутентификация']
const ellipsisIndex = computed(() =>
getEllipsisBeforeIndex({ total: items.value.length, maxItems: props.maxItems ?? 4 }),
)
const hidden = computed(() =>
expanded.value ? [] : getCollapsedHiddenIndexes({ total: items.value.length, maxItems: props.maxItems ?? 4 }),
)
<!-- Vue3 -->
<template>
<nav aria-label="Хлебные крошки">
<LhBreadcrumb :max-items="4">
<LhBreadcrumbItem to="/">Главная</LhBreadcrumbItem>
<LhBreadcrumbItem to="/system">Система</LhBreadcrumbItem>
<LhBreadcrumbItem to="/system/security">Безопасность</LhBreadcrumbItem>
<LhBreadcrumbItem>Двухфакторная аутентификация</LhBreadcrumbItem>
</LhBreadcrumb>
</nav>
</template>
// React: данные пути обычно выводятся из таблицы роутов (useMatches / собственный route meta),
// а не объявляются поэлементно вручную — отображение хлебных крошек на роуты
// является бизнес-соглашением
// uniapp: <navigator> — нативный компонент перехода; на стороне мини-программ нет
// семантики <nav>, использование view + aria-label не работает — это приемлемо
// (экосистема скринридеров в мини-программах иная), главное — сохранить структуру DOM
Класс Семантика
.lh-breadcrumb контейнер (inline-flex)
.lh-breadcrumb__item один элемент
.lh-breadcrumb__item--link состояние ссылки (изменение цвета при hover)
.lh-breadcrumb__item--last последний элемент (текущая страница, жирный или основной цвет)
.lh-breadcrumb__separator разделитель (обычно CSS-псевдоэлемент)
.lh-breadcrumb__ellipsis многоточие (кликабельное, разворачивает)

Цвет текста --lh-text-regular, цвет ссылки при hover --lh-primary-color, последний элемент --lh-text-primary; размер шрифта --lh-font-size-base.

  • maxItems включает многоточие: maxItems: 4 фактически показывает «первый элемент + многоточие + последние 3 элемента» = 5 элементов, а видимых текстовых элементов будет 4. Настраивайте по количеству видимых текстовых элементов.
  • Динамичность сворачивания: длина пути меняется с глубиной навигации (total в getEllipsisBeforeIndex динамический), а maxItems фиксирован — вручную определять момент сворачивания не нужно, функция обрабатывает это автоматически.
  • Взаимодействие разворачивания многоточия: два распространённых подхода — ① клик по многоточию разворачивает всё на месте (переключение состояния expanded); ② клик открывает popover со скрытыми элементами. Первый проще, второй экономит место; выбирайте по глубине пути.
  • Источник данных пути: хлебные крошки обычно выводятся из таблицы роутов, а не пишутся вручную — в meta каждого роута прописывается title, а на уровне компонента агрегируются meta предков текущего пути. core отвечает только за алгоритм рендеринга, интеграция с роутингом — задача UI-слоя.
  • Последний элемент не рендерится ссылкой: указание текущей страницы на саму себя лишено смысла (и isLink: false). Обратите внимание: класс --link в getBreadcrumbItemClassNames управляется isLink, и у последнего элемента его естественно нет.
  • Сокращение слишком длинных заголовков: при избыточной длине заголовка одного уровня используется CSS text-overflow: ellipsis (стили UI-слоя); это другое «сокращение», нежели сворачивание пути — сворачивание экономит число уровней, а CSS — ширину одного уровня.
  • Не используйте хлебные крошки как основную навигацию: это вспомогательный индикатор позиции, главный вход остаётся за меню. На мобильных при нехватке места хлебные крошки часто опускают или оставляют только кнопку «Назад».
@lh-ui/share/core/breadcrumb → getEllipsisBeforeIndex( )
返回值
(填好参数后点运行)

Copyright © 2024-2026 FengMo WenXue