Navigation/Flyout-Menü

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Gerade auf kleinen Bildschirmen und für Screenreader ist es oft wünschenswert, gleich zum Inhalt zu kommen, ohne sich lange mit der Navigation aufzuhalten. Hier gibt es zwei Möglichkeiten:

  • ein Skip-Link ermöglicht es, die Navigation zu überspringen
  • ein Toggle-Menü versteckt die Navigation und klappt sie nur bei Bedarf aus. Wir zeigen Ihnen zwei Ansätze. Zuerst mit Button und etwas JavaScript, dann eine Variante mit details ohne JavaScript.

Skiplink

Auf einem normalen Monitor ist die Dropdown-Navigation im Ausgangszustand eingeklappt und lässt sich mit einem Blick nach unten übergehen, während auf Screenreader angewiesene Benutzer sich durch alle Links durchtabben müssten. Mit einem Skipklink, der ganz am Anfang im Markup steht und auf den Hauptinhalt gerichtet ist, kannst du das Menü überspringen. Im Normalzustand ist der Skip-Link per CSS außerhalb des sichtbaren Bereichs positioniert.[1]


Skip-Link, der direkt zum Inhalt führt
<a id="skip-link" class="visually-hidden" href="#content">zum Inhalt</a>

<nav>
  <ul>
    <li><a aria-current="page">Home</a></li>
    <li><a href="#">Seite 2</a></li>
    <li><a href="#">Seite 3</a></li>
    <li><a href="#">Seite 4</a></li>
    <li><a href="#">Kontakt</a></li>
</nav>

<main id="content">

Skiplinks werden am logischen Beginn des Dokuments notiert und enthalten zum Beispiel:

  • zum Inhalt
  • zur Navigation
  • zur Sitesearch

Über die Klasse visually-hidden wird der Skiplink für Sehende ausgeblendet. Mit Software wie VoiceOver und anderen Screenreadern wird solch ein Link einfach als „Link zum Inhalt“ vorgelesen.

Beachte: Wenn der Skiplink mit der Tastatur angesteuert wird, sollte der Linktext ebenfalls sichtbar sein!

CSS/Tutorials/Sichtbarkeit von Elementen‎‎

Flyout-Menü mit toggle-Button

Um Platz für Inhalte zu schaffen, soll die Navigation nur bei Bedarf eingeblendet werden. Deshalb erweitern wir unsere responsive Navigation aus dem letzten Kapitel um einen toggle-Button. Im Grundzustand soll nur ein Navicon sichtbar sein - erst ein Klick auf dieses öffnet das Menü.[2][3]

Genau wie beim inklusiven Dropdown-Menü soll eine Liste aus Links durch JavaScript erweitert werden:


HTML-Markup

Es mag trivial scheinen, aber eine sorgfältige Auswahl der verwendeten HTML-Elemente erleichtert die volle Zugänglichkeit, damit das Menü von allen Benutzern mit Maus, Tastatur und Screenreadern „gelesen“ und bedient werden kann.

zugängliche HTML-Grundstruktur ansehen …
<nav>
  <button>Menü</button>
  <ul>
    <li><a aria-current="page">Home</a></li>
    <li><a href="#">Seite 2</a></li>
    <li><a href="#">Seite 3</a></li>
    <li><a href="#">Seite 4</a></li>
    <li><a href="#">Kontakt</a></li>
</nav>

Wir verwenden, wie im Grundstruktur-Kapitel besprochen:

  • ein nav-Element als Container.
    Dies hat gegenüber einem inhaltsleeren div den Vorteil, dass Screenreader dem Nutzer die Bedeutung des Elements vorlesen.
  • einen button[4]
    Dieser hat gegenüber einem inhaltsleeren span bereits ein Standardverhalten implementiert. Man kann ihn mit Maus oder Tastatur anwählen. Dann erhält er den focus.
    Durch die Positionierung innerhalb des nav-Elements wird er diesem direkt zugordnet.
  • eine ul, die die Listenelemente mit den darin befindlichen Links enthält.
Beachte: Wichtig ist nun, dass nicht das nav-Element im Ganzen, sondern nur die ul mit den Links ausgeblendet werden. So können Nutzer von Screenreadern sofort erkennen, dass es eine Navigation gibt und dass der Button zu ihr gehört und diese steuert.

Toggle-Design: Navicon

Im allgemeinen Gebrauch haben sich als Symbol oder Icon für einen Navigations-Button drei waagerechte Striche durchgesetzt. Deshalb wird ein solches Menü auch oft „Burgermenü“ oder „Hamburgermenü“ genannt. Du kannst bei Verwendung der Zeichencodierung utf-8 entweder direkt „☰“ oder &#9776; eingeben.

<button>
  Menu
  <span class="close-icon">×</span>
  <span class="burger-icon">☰</span>
</button>

Bei diesem Ansatz fügen wir dem Button sowohl ein x als Schließen-Symbol und ein „Hamburger-Symbol“ hinzu. Sobald der Benutzer den Button betätigt, wird das Hamburger-Symbol ausgeblendet und durch das Schließen-Symbol ersetzt.

Diese Methode hat mehrere Nachteile:

  • Bildschirmlesegeräte werden diese Symbole mit ihrem Namen vorlesen, was sehr verwirrend ist.
    • ×: menu times, toggle button navigation 1 item
    • ☰: menu trigram for heaven, toggle button navigation 1 item

Zumindest sollten sie vor Screenreadern mit dem Attribut aria-hidden verborgen werden.

  • Durch die unterschiedliche Größe der Zeichen ist eine Formatierung mit CSS umständlich

Einfacher und zugänglicher ist es, den Text des Buttons visuell zu verstecken. Dies kann mit einem Pseudoelement oder einem SVG als Hintergrundgrafik realisiert werden.

Steuerung mit JavaScript

Nun soll die Navigation mit CSS versteckt und mit JavaScript wieder eingeblendet werden. Dabei soll die Navigation immer bedienbar sein, auch wenn CSS oder JS nicht geladen oder ausgeführt werden können:[5]

Dies ist progressive enhancement , eine schrittweise Verbesserung

  • Nur HTML: In diesem Fall wird der Benutzer nur die Navigationsliste sehen und bedienen können. Jetzt ist unsere JavaScript-gesteuerte Schaltfläche nutzlos und tut nichts.
  • HTML + CSS: Das Gleiche wie vorher, aber mit einem besseren Aussehen und Gefühl.
  • HTML, CSS + JavaScript: Das JavaScript erzeugt den Button und blendet die Navigation aus.
    Wir können auf den Button klicken und die Anzeige des Menüs ein- und ausschalten.

Button erzeugen

Unser JavaScript erweitert jede Navigationsliste um einen Button, der dann auf Nutzerinteraktionen reagieren kann:

JavaScript: Button erzeugen ansehen …
 
function flyoutExtension () {
	const menu = document.querySelector('nav ul');
	menu.insertAdjacentHTML('beforebegin', `
	<button id="toggle" aria-expanded="false" aria-controls="menu">
		Menu auf- und zuklappen
	</button>`);
	menu.classList.add('menu');
}

Mit querySelector('nav ul') finden wir das erste Vorkommen im HTML und weisen es der Konstante menu zu.

Jetzt wird mit insertAdjacentHTML unser Button eingefügt. Die ul erhält mit classList.add noch eine Klasse menu.

Ergebnis: Wir haben einen Button mit Text und unser noch sichtbares Menü.

Zustand mit ARIA-Attributen anzeigen

Ist der Button nun geklickt oder nicht? Das kann man nicht gleich erkennen. ARIA-Attribute können interaktive Elemente zugänglicher machen, indem sie Zustände anzeigen:

ARIA-Attribute ansehen …
  <nav>
    <button id="toggle" aria-expanded="false" aria-controls="menu">Menu</button>
    <ul id="menu">
     ...

Der eben erzeugte Button hat neben seiner id nun zwei Attribute:

  • aria-expanded zeigt an, ob der Button gerade aus- oder eingeklappt ist.
  • aria-controls identifizert ein Element, dessen Inhalt durch das aktuelle Element kontrolliert wird.

Die Liste innerhalb des nav-Elements erhält die id menu und wird so mit dem Button verknüpft!

Die jeweiligen Werte werden durch das Script geändert.

auf Klicks reagieren ansehen …
	const toggle = document.querySelector('#toggle');
	toggle.addEventListener('click', function() {
		const currentState = toggle.getAttribute('aria-expanded');
		const newState = currentState === 'true' ? 'false' : 'true';
		toggle.setAttribute('aria-expanded', newState);
	});

Wenn Du das Beispiel mit Klick auf [Vorschau] in einem neuen Tab öffnest; kannst du das HTML im Seiteninspektor inspizieren.

Mit jedem Klick werden die aria-expanded-Zustände neu gesetzt.

CSS

Ein Screenreader liest den Button mit seinem Text 'Menü auf- und zuklappen' vor - visuell können wir den Text mit der schon erwähnten Klasse visually-hiddenverstecken und durch ein Navicon, bzw. ein Schließen-'X' ersetzen:

Das fertige Beispiel: Ein Navicon weist auf die (versteckte) Navigation hin! ansehen …
[aria-expanded] {
  width:2em;
  height: 2em;
  background: transparent;
  color: transparent;
  border: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M1,2 h7 M1,5 h7 M1,8 h7' fill='none' stroke='gold' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}
[aria-expanded="true"] {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M2,2 l6,6 M8,2 l-6,6' fill='none' stroke='%23c82f04' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

[aria-expanded=false] ~ ul {
 display: none;
}

[aria-expanded=true] ~ ul {
 display: visible;
}

Der Button erhält transparente Rahmen und Hintergründe. Über den Attributspräsenz-Selektor [aria-expanded] wird nun unser Navicon als SVG-Hintergrundbild eingeblendet.

Ändert sich der Wert durch Anklicken, wird nun das Schließen-Symbol angezeigt.

Über den Geschwisterselektor ~ wird das nach dem Button folgende Menü versteckt oder sichtbar geschaltet - ohne weiteres Setzen von Klassen!

Kopiervorlage für Ungeduldige

Dieses Script fügt sowohl die Interaktivität als auch das dafür nötige CSS in deine Webseiten ein. Da es auf allen (Unter)-seiten verfügbar sein sollte, ist es empfehlenswert, dies nicht wie im Beispiel in den head des Dokuments, sondern als eigenes Script-Dokument extern einzubinden.

Empfehlung:
Speicher das Script als eigenes Dokument mit der Dateiendung flyout-menu.js ab und binde es in jede deiner Seiten ein:
<script src="js/flyout-menu.js"></script>
Passe den Pfad an Deine Dateistruktur an.
Das fertige Beispiel zum Kopieren ansehen …
document.addEventListener('DOMContentLoaded', function () {

		flyoutExtension();
	
function flyoutExtension () {
	const menu = document.querySelector('nav ul');
	menu.insertAdjacentHTML('beforebegin', `
	<button id="toggle" aria-expanded="false" aria-controls="menu">
		<span class="visually-hidden">Menu auf- und zuklappen</span>
	</button>`);
	menu.classList.add('menu');

	const toggle = document.querySelector('#toggle');
	toggle.addEventListener('click', function() {
		const currentState = toggle.getAttribute('aria-expanded');
		const newState = currentState === 'true' ? 'false' : 'true';
		toggle.setAttribute('aria-expanded', newState);
		const isExpanded = toggle.getAttribute('aria-expanded') === 'true';
        menu.style.display = isExpanded ? 'block' : 'none';
	});
	
}
 const cssRules = `
 .visually-hidden,
[visually-hidden="true"] {
	position: absolute !important;
	clip-path: rect(1px, 1px, 1px, 1px) !important;
	padding: 0 !important;
	border: 0 !important;
	height: 1px !important;
	width: 1px !important;
	overflow: hidden !important;
}
.visually-hidden:focus,
[visually-hidden="true"]:focus {
    position: static !important; 
    clip-path: none !important; 
    padding: 0.5em; 
    border: medium solid blue; 
    z-index: 1000; 
    width: auto; 
    height: auto; 
}

[aria-expanded] {
  width:2em;
  height: 2em;
  background: transparent;
  color: transparent;
  border: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M1,2 h7 M1,5 h7 M1,8 h7' fill='none' stroke='gold' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}
[aria-expanded="true"] {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 9 9'%3E%3Cpath d='M2,2 l6,6 M8,2 l-6,6' fill='none' stroke='%23c82f04' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
}

[aria-expanded=false] ~ ul {
	font-size: 0;
	height:0;
	transition: all 1s;
}

[aria-expanded=true] ~ ul {
	font-size: 1em;
	height: 100%;
	transition: all 1s;
}

:root {
	--background-color: #113;
	--accent1-color: gold;
	--accent2-color: darkred;	
	--text-color: black;
	--blue: #337599;
	--blue4: #adc8d6;
}`;

	const stylesheet = document.createElement('style');
	stylesheet.textContent = cssRules;
	document.querySelector('html > head').appendChild(stylesheet);		

});

Das CSS befindet sich in der CSSRules-Variablen und wird beim Initialisieren in ein dynamisch erzeugtes style-Element eingefügt.

Empfehlung:
Wenn Du die Farben ändern willst, entferne die Festlegung im :root{}-Regelsatz und lege sie in Deinem Stylesheet fest.

Alternative: details

Die Toggle-Funktionalität des Flyout-Menüs wurde bisher über den Button und das dazugehörige JavaScript abgewickelt.

Seit mehreren Jahren gibt es so etwas auch als natives HTML-Element. Mit details und summary kannst du Inhalte auf- und wieder zuklappen. Dieses Standardverhalten macht sich folgendes Beispiel zunutze, das so auf JavaScript verzichten kann:

Flyout-Menü mit details/summary ansehen …
<nav role="navigation">
    <details>
    <summary><span>Menü</span></summary>
    <ul id="menu">
      <li><a aria-current="page">Home</a></li>
      <li><a href="#">Seite 2</a></li>
      <li><a href="#">Seite 3</a></li>
      <li><a href="#">Seite 4</a></li>
      <li><a href="#">Kontakt</a></li>
    </ul>
    </details>
</nav>

Dieses Beispiel ist auch mit Tastatur und Screenreader benutzbar. Einige 2019 noch vorhandene Bugs bei NVDA und Firefox (<71) wurden mittlerweile behoben.[6]

Bei :hover wird das summary-Element allerdings nicht gekennzeichnet. Dies wurde hier durch CSS ergänzt:

Kennzeichnung von summary bei hover ansehen …
nav summary {
  cursor:pointer;
}
nav summary:hover {
  font-size:2.2em;
  text-shadow: 2px 2px 5px white;
}

Achtung!

Screenreader lesen das details-Element als „“ vor, was natürlich abschreckend ist. Andererseits sind Nutzer von Screenreadern daran gewöhnt und blenden eine solche Ansage aus.

Trotzdem würde ich persönlich die obere Variante mit JavaScript empfehlen.

--Matthias Scharwies (Diskussion) 09:27, 19. Feb. 2023 (CET)

Fazit

Ein solches Menü eignet sich gleichermaßen für OnePager wie für kleinere Webauftritte. Allerdings gibt es Probleme mit dem Vorlesen durch Screenreader, sodass die obere Variante mit JavaScript zu bevorzugen ist.

Für größere DropDown-Menüs stellt sich die Problematik des Auf- und Zuklappens bei jeder neuen Unterliste. Hier empfiehlt sich der Einsatz einer JavaScript-Lösung, wie sie im vorigen Kapitel vorgestellt wurde.
Das 2019 geschriebene Tutorial einer zugänglichen Dropdown-Navigation mit summary und details wurde aus diesen Gründen depubliziert.


Quellen

  1. SELF-Blog: Skipper ahoj! von Gunnar Bittersmann am 15.07.2018
  2. W3C: Fly-out Menus Tutorial der WAI
  3. Die vorliegende Navigation basiert auf einem Entwurf von Ahmad Shadeed für a11ymatters.com.
    a11ymatters.com: Accessible Mobile Navigation von Ahmad Shadeed
  4. Ahmad Shadeed überlegte auch einen Link zu verwenden, dessen href-Attribut auf die id des ul-Elements zeigte. Insoweit ok, aber zum Schließen des Menüs benötigt man eben einen Button und keinen Link!
  5. Ahmad Shadeed zeigte in seinem Artikel in Kapitel 3- Don’t hide the <nav> element, wie man die Navigation nun per CSS aus- und mit JavaScript wiedereinblendet. Dies scheint logisch, führt aber zu Problemen, wenn aus Gründen kein JavaScript geladen oder ausgeführt werden kann. Dann gibt es gar keine Navigation.
  6. Accessible accordions part 2 – using <details> and <summary> von Graham Arnfield