Smooth Scrolling und Barrierefreiheit

Avatar of Heather Migliorisi
Heather Migliorisi am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Smooth scrolling (die animierte Positionsänderung innerhalb des Viewports von einem ausgehenden Link zu einem Zielanker) kann ein schönes Interaktionsdetail sein, das einer Website hinzugefügt wird und dem Erlebnis ein poliertes Gefühl verleiht. Wenn Sie mir das nicht glauben, sehen Sie sich an, wie viele Leute auf den Smooth Scrolling-Snippet hier auf CSS-Tricks reagiert haben.

smooth scroll vs abrupt jump
Smooth Scrolling im Vergleich zu abrupten Sprüngen

Unabhängig davon, wie Sie die Funktion implementieren, gibt es einige Barrierefreiheitsprobleme, die angegangen werden sollten: Fokusmanagement und Animation.

Fokusmanagement

Es ist wichtig sicherzustellen, dass alle Inhalte allein mit der Tastatur zugänglich sind, da einige Benutzer 100% auf die Tastatur für die Navigation angewiesen sind. Wenn also ein Tastaturnutzer durch die Inhalte navigiert und auf einen Link stößt, der Smooth Scrolling verwendet, sollte er diesen verwenden können, um zum Zielanker-Element zu navigieren.

Mit anderen Worten: Wenn Sie einem Link folgen, sollte der Tastatur-Fokus ihm ebenfalls folgen und in der Lage sein, auf das nächste Element nach dem Ziel zuzugreifen. Hier ist ein Beispiel für Links zu Seitenankern, bei denen der Fokus erhalten bleibt, da kein JavaScript verwendet wird

Example where focus is maintained
Standard-Browserverhalten mit Links zu Seitenankern und korrekt erhaltenem Fokus.

Probieren Sie es selbst aus: Verwenden Sie die Tabulatortaste, um mit dieser Demo zu navigieren. Bitte beachten Sie, dass Safari/WebKit einen offenen Fehler bezüglich des Tastaturfokus aufweist.

Original jQuery-Beispiel

Schauen wir uns das jQuery-Beispiel aus dem Originalbeitrag an

$(function() {
  $('a[href*="#"]:not([href="#"])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000);
        return false;
      }
    }
  });
});

Dies ist auf einer Seite des W3C implementiert

Smooth scroll on the W3C website
Der Skip-Nav sollte :focus auf den Inhalt haben, aber dies ändert den Fokus nicht

Hier sehen wir, dass der Link „Skip to Content“ den Fokus nicht auf den Inhalt setzt, zu dem navigiert wurde. Wenn wir also dieses Beispiel verwenden, verschlechtern wir die Navigation für Benutzer, die die Tastatur verwenden, da der Benutzer erwartet, zu dem Zielinhalt zu navigieren, dies aber nicht tut, da der Fokus nicht aktualisiert wird, um die Änderung widerzuspiegeln.

Probieren Sie es selbst aus, indem Sie die Tabulatortaste verwenden, um mit dieser Demo zu navigieren.

Was lief falsch?

Warum funktioniert das nicht? Wir verwenden JavaScript, um das normale Browser-Verhalten von Links zu übernehmen (beachten Sie, dass sich die URL nie mit dem /#target aktualisiert), was bedeutet, dass wir den Fokus mit JavaScript setzen müssen. In jQuery wäre das $(target).focus();.

Damit dies auf nicht fokussierbaren Zielelementen (Abschnitt, div, span, h1-6 usw.) funktioniert, müssen wir ihnen tabindex="-1" zuweisen, um $(target).focus(); verwenden zu können. Wir können entweder tabindex="-1" direkt auf nicht fokussierbare Zielelemente im HTML-Markup setzen oder es mit JavaScript hinzufügen, wie hier gezeigt.

$(function() {
  $('a[href*="#"]:not([href="#"])').click(function() {
    if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
      var target = $(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
      if (target.length) {
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 1000);
        target.focus(); // Setting focus
        if (target.is(":focus")){ // Checking if the target was focused
          return false;
        } else {
          target.attr('tabindex','-1'); // Adding tabindex for elements not focusable
          target.focus(); // Setting focus
        };
        return false;
      }
    }
  });
});

Probieren Sie es selbst aus, indem Sie die Tabulatortaste verwenden, um mit dieser Demo zu navigieren. Vergessen Sie nicht Ihre :focus-Styling!

Focus functionality on scroll on the W3C site

Ein besserer Weg?

Es könnte für Benutzer im Allgemeinen besser sein, wenn wir diese Funktion handhaben, ohne das normale Browser-Navigationsverhalten zu kapern. Wenn Sie beispielsweise einem Link folgen, können Sie mit der Zurück-Schaltfläche des Browsers zurückkehren. Außerdem können Sie die aktuelle URL als Lesezeichen speichern (oder kopieren und einfügen), und der Browser wird von dem zuletzt geklickten Link zu diesem bestimmten Ziel navigieren.

// URL updates and the element focus is maintained
// originally found via in Update 3 on http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links

// filter handling for a /dir/ OR /indexordefault.page
function filterPath(string) {
  return string
    .replace(/^\//, '')
    .replace(/(index|default).[a-zA-Z]{3,4}$/, '')
    .replace(/\/$/, '');
}

var locationPath = filterPath(location.pathname);
$('a[href*="#"]').each(function () {
  var thisPath = filterPath(this.pathname) || locationPath;
  var hash = this.hash;
  if ($("#" + hash.replace(/#/, '')).length) {
    if (locationPath == thisPath && (location.hostname == this.hostname || !this.hostname) && this.hash.replace(/#/, '')) {
      var $target = $(hash), target = this.hash;
      if (target) {
        $(this).click(function (event) {
          event.preventDefault();
          $('html, body').animate({scrollTop: $target.offset().top}, 1000, function () {
            location.hash = target; 
            $target.focus();
            if ($target.is(":focus")){ //checking if the target was focused
              return false;
            }else{
              $target.attr('tabindex','-1'); //Adding tabindex for elements not focusable
              $target.focus(); //Setting focus
            };
          });       
        });
      }
    }
  }
});
Focus working on the W3C site
Beispiel, das zeigt, dass sich die URL mit jedem angeklickten Anker aktualisiert

Hier aktualisiert sich die URL mit jedem angeklickten Anker. Probieren Sie es selbst aus, indem Sie die Tabulatortaste verwenden, um mit dieser Demo zu navigieren.

Natives Beispiel

Schauen wir uns das native Browser-Beispiel aus dem CSS-Tricks-Post an. (Es gibt auch einen Polyfill.)

document.querySelector('#target-of-thing-clicked-on').scrollIntoView({ 
  behavior: 'smooth' 
});

Leider stoßen wir bei dieser Methode auf dasselbe Problem wie bei der jQuery-Methode, bei der die Seite im Viewport scrollt, aber der Tastaturfokus nicht aktualisiert wird. Wenn wir also diesen Weg gehen wollen, müssten wir immer noch .focus() setzen und sicherstellen, dass nicht fokussierbare Zielelemente tabindex="-1" erhalten.

Eine weitere Überlegung ist das Fehlen einer Callback-Funktion für das Ende des Scrollens. Das kann ein Problem sein oder auch nicht. Sie würden den Fokus gleichzeitig mit dem Scrollen verschieben und nicht am Ende, was vielleicht ein wenig seltsam ist oder auch nicht. Wie auch immer, es wird Arbeit zu tun geben!

Bewegung und Barrierefreiheit

Manche Menschen können buchstäblich krank werden von der schnellen Bewegung auf dem Bildschirm. Ich würde eine langsame Geschwindigkeit der Bewegung empfehlen, denn wenn der Benutzer über viel Inhalt springt, kann dies zu Schwindelgefühlen führen, wenn es zu schnell ist.

Außerdem ist es keine schlechte Idee, Benutzern eine Möglichkeit zu bieten, Animationen auszuschalten. Glücklicherweise hat Safari 10.1 die Reduced Motion Media Query eingeführt, die Entwicklern eine Methode bietet, Animationen so einzubinden, dass sie auf Browser-Ebene deaktiviert werden können.

/* JavaScript MediaQueryList Interface */
var motionQuery = window.matchMedia('(prefers-reduced-motion)');
if (motionQuery.matches) {
  /* reduce motion */
}
motionQuery.addListener( handleReduceMotionChanged );

Leider haben andere Browser diese Funktion noch nicht implementiert. Bis die Unterstützung über einen einzigen Browser hinaus verbreitet ist, können wir dem Benutzer über die Benutzeroberfläche eine Option zum Aktivieren/Deaktivieren von Animationen anbieten, die Probleme verursachen könnten.

<label>
  <input type="checkbox" id="animation" name="animation" checked="checked">
  Enable Animation
</label> 
$(this).click(function(event) {
  if ($('#animation').prop('checked')) {
    event.preventDefault();
    $('html, body').animate({scrollTop: $target.offset().top}, 1000, function() {
      location.hash = target;
      $target.focus();
      if ($target.is(":focus")) {
        return !1;
      } else {
        $target.attr('tabindex', '-1');
        $target.focus()
      }
    })
  }
});

Probieren Sie es selbst aus mit dieser Demo.