Аккордеон объединяет связанный контент в раскрывающиеся и сворачивающиеся разделы, сокращая прокрутку страницы и помогая пользователям сосредоточиться на нужной информации. У каждого раздела есть кнопка-триггер и панель с контентом. Нажатие на триггер переключает видимость связанной с ним панели.
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="basic-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
Аккордеоны хорошо подходят для организации контента в логические группы, когда пользователям обычно нужно просматривать по одному разделу за раз.
Используйте аккордеоны, когда нужно:
Показывать часто задаваемые вопросы с несколькими вопросами и ответами
Разбивать длинные формы на удобные для восприятия разделы
Сокращать прокрутку на страницах с большим количеством контента
Постепенно раскрывать связанную информацию
Не используйте аккордеоны, когда нужно:
Создать навигационное меню (вместо этого используйте компонент Menu)
Создать интерфейс с вкладками (вместо этого используйте компонент Tabs)
Показать один сворачиваемый раздел (вместо этого используйте шаблон раскрывающегося блока)
Пользователям нужно одновременно видеть несколько разделов (рассмотрите другой вариант компоновки)
Возможности
Режимы раскрытия — управление тем, может ли одновременно быть открыта одна или несколько панелей
Клавиатурная навигация — переход между триггерами с помощью клавиш со стрелками, Home и End
Отложенный рендеринг — контент создаётся только при первом раскрытии панели, что повышает производительность при начальной загрузке
Отключённые состояния — отключение всей группы или отдельных триггеров
Управление фокусом — настройка возможности перевода клавиатурного фокуса на отключённые элементы
Программное управление — раскрытие, сворачивание и переключение панелей из кода компонента
Поддержка RTL — автоматическая поддержка языков с письмом справа налево
Примеры
Режим одиночного раскрытия
Установите [multiExpandable]="false", чтобы одновременно могла быть открыта только одна панель. При открытии новой панели любая ранее открытая панель автоматически закрывается.
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="basic-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="material-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" [expanded]="true">
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
Этот режим хорошо подходит для часто задаваемых вопросов и других ситуаций, когда нужно, чтобы пользователи сосредоточивались на одном ответе за раз.
Режим множественного раскрытия
Установите [multiExpandable]="true", чтобы одновременно могли быть открыты несколько панелей. Пользователи могут раскрыть столько панелей, сколько нужно, не закрывая остальные.
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="basic-accordion">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger" [expanded]="true">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="material-accordion">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger" [expanded]="true">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
Этот режим полезен для разделов форм или когда пользователям нужно сравнивать контент в нескольких панелях.
ПРИМЕЧАНИЕ: По умолчанию для входного параметра multiExpandable используется значение true. Явно задайте false, если хотите использовать режим одиночного раскрытия.
Отключённые элементы аккордеона
Отключайте отдельные триггеры с помощью входного параметра disabled. Настройте поведение отключённых элементов при клавиатурной навигации с помощью входного параметра softDisabled группы аккордеона.
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="basic-accordion">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" disabled>
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q3" #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
import {Component} from '@angular/core';
import {
AccordionGroup,
AccordionTrigger,
AccordionPanel,
AccordionContent,
} from '@angular/aria/accordion';
@Component({
selector: 'app-root',
templateUrl: 'app.html',
styleUrl: 'app.css',
imports: [AccordionGroup, AccordionTrigger, AccordionPanel, AccordionContent],
})
export class App {}
<div ngAccordionGroup class="material-accordion">
<h3>
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger1.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
panel is visible and "false" when the content is hidden. This is a crucial state indicator
for screen reader users.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" disabled>
How do you link the button to the content it controls?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger2.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
id of the related content panel. This establishes a programmatic relationship, allowing
assistive technologies to jump directly to the relevant content.
</p>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
class="material-symbols-outlined expand-icon"
[class.expand-icon__expanded]="trigger3.expanded()"
translate="no"
>keyboard_arrow_up</span
>
</span>
</h3>
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
appropriate <code>aria-level</code> to define the structure. This ensures the accordion
section is recognized as a section header, making the page structure navigable for users.
</p>
</ng-template>
</div>
</div>
Когда задано [softDisabled]="true" (значение по умолчанию), отключённые элементы могут получать фокус, но их нельзя активировать. Когда задано [softDisabled]="false", отключённые элементы полностью пропускаются при клавиатурной навигации.
Отложенный рендеринг контента
Используйте директиву ngAccordionContent на элементе ng-template, чтобы отложить рендеринг контента до первого раскрытия панели. Это повышает производительность аккордеонов с ресурсоёмким контентом, например изображениями, диаграммами или сложными компонентами.
<div ngAccordionGroup>
<div>
<button ngAccordionTrigger [panel]="panel1">Trigger Text</button>
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<!-- This content only renders when the panel first opens -->
<img src="large-image.jpg" alt="Description" />
<app-expensive-component />
</ng-template>
</div>
</div>
</div>
По умолчанию контент остаётся в DOM после сворачивания панели. Задайте [preserveContent]="false", чтобы удалять контент из DOM при закрытии панели.
Тестирование
Angular Aria предоставляет тестовые оболочки компонентов для тестирования компонентов аккордеона. Ниже приведён пример использования тестовых оболочек в тесте компонента:
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HarnessLoader} from '@angular/cdk/testing';
import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed';
import {AccordionGroupHarness} from '@angular/aria/accordion/testing';
import {MyAccordionComponent} from './my-accordion'; // Your component
describe('MyAccordionComponent', () => {
let fixture: ComponentFixture<MyAccordionComponent>;
let loader: HarnessLoader;
beforeEach(async () => {
TestBed.configureTestingModule({
imports: [MyAccordionComponent],
});
fixture = TestBed.createComponent(MyAccordionComponent);
await fixture.whenStable();
loader = TestbedHarnessEnvironment.loader(fixture);
});
it('should allow expanding panels', async () => {
// Load the accordion group harness
const group = await loader.getHarness(AccordionGroupHarness);
// Get all individual accordions (items) in the group
const accordions = await group.getAccordions();
expect(accordions.length).toBe(3);
// Verify initial state (first expanded, others collapsed)
expect(await accordions[0].isExpanded()).toBe(true);
expect(await accordions[1].isExpanded()).toBe(false);
// Expand the second panel
await accordions[1].expand();
// Verify updated state
expect(await accordions[1].isExpanded()).toBe(true);
// If multiExpandable is false, the first one should now be collapsed
expect(await accordions[0].isExpanded()).toBe(false);
});
});
API
AccordionGroup
Директива-контейнер, которая управляет клавиатурной навигацией и поведением раскрытия для группы элементов аккордеона.
Входные параметры
Свойство
Тип
По умолчанию
Описание
disabled
boolean
false
Отключает все триггеры в группе
multiExpandable
boolean
true
Могут ли несколько панелей быть раскрыты одновременно
softDisabled
boolean
true
Если задано true, отключённые элементы могут получать фокус. Если задано false, они пропускаются
wrap
boolean
false
Переходит ли клавиатурная навигация от последнего элемента к первому и наоборот
Методы
Метод
Параметры
Описание
expandAll
нет
Раскрывает все панели (работает, только если multiExpandable имеет значение true)
collapseAll
нет
Сворачивает все панели
AccordionTrigger
Директива, применяемая к элементу кнопки, которая переключает видимость панели.