平滑滚动与无障碍性

Avatar of Heather Migliorisi
Heather Migliorisi

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

平滑滚动(从起始链接到目标锚点的视口中位置的动画变化)可以作为网站添加的一个不错的交互细节,为用户体验带来精致的感觉。如果您不相信我,看看有多少人对 CSS-Tricks 上的 平滑滚动代码片段 做出了回应。

smooth scroll vs abrupt jump
平滑滚动与突然跳转

无论您如何实现此功能,都应解决一些无障碍性问题:焦点管理和动画。

焦点管理

务必确保所有内容都只能通过键盘访问,因为某些用户完全依赖键盘进行导航。因此,当键盘用户浏览内容并点击使用平滑滚动的链接时,他们应该能够使用它导航到目标锚元素。

换句话说,当您点击链接时,键盘焦点也应该跟随它,并能够访问目标后的下一个元素。这是一个链接到页面锚点的示例,其中焦点保持不变,因为没有使用 JavaScript

Example where focus is maintained
链接到页面锚点时的默认浏览器行为,焦点得到正确维护。

亲自尝试一下:使用 Tab 键通过 此演示 进行导航。请注意,Safari/WebKit 存在一个关于键盘焦点的未解决的错误

原始 jQuery 示例

让我们看看来自 原始帖子 的 jQuery 示例

$(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;
      }
    }
  });
});

这是在 W3C 的一个页面上实现的

Smooth scroll on the W3C website
跳过导航应该在内容上:focus,但焦点没有改变

在这里,我们看到 “跳至内容”链接 没有将焦点设置到导航到的内容上。因此,如果我们使用此示例,我们会使键盘用户的导航体验变差,因为用户期望导航到目标内容,但他们没有,因为焦点没有更新以反映更改。

亲自尝试一下,使用 Tab 键通过 此演示 进行导航。

哪里出错了?

为什么这不起作用?我们正在使用 JavaScript 接管正常的浏览器链接行为(请注意,URL 不会随着 /#target 更新),这意味着我们需要使用 JavaScript 设置焦点。在 jQuery 中,这将是$(target).focus();

为了使这在不可聚焦的目标元素(section、div、span、h1-6 等)上正常工作,我们必须在它们上面设置tabindex="-1"以便能够使用$(target).focus();。我们可以在 html 标记中直接在不可聚焦的目标元素上添加tabindex="-1",也可以像这里看到的那样使用 JavaScript 添加它。

$(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;
      }
    }
  });
});

亲自尝试一下,使用 Tab 键通过 此演示 进行导航。不要忘记你的 :focus 样式!

Focus functionality on scroll on the W3C site

更好的方法?

如果我们不劫持正常的浏览器导航行为来处理此功能,可能对用户更有利。例如,如果您点击链接,您可以使用浏览器后退按钮返回。此外,您可以将当前 URL 添加到书签(或复制粘贴),浏览器将从您上次点击的链接转到该特定目标。

// 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
显示 URL 在每次点击锚点时都会更新的示例

在这里,URL 在每次点击锚点时都会更新。亲自尝试一下,使用 Tab 键通过 此演示 进行导航。

原生示例

让我们看看来自 CSS-Tricks 帖子 的原生浏览器示例。(还有一个 polyfill。)

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

不幸的是,使用这种方法,我们遇到了与 jQuery 方法相同的问题,即页面在视口中滚动,但不会更新键盘焦点。因此,如果我们想走这条路,我们仍然必须设置.focus()并确保不可聚焦的目标元素接收tabindex="-1"

这里另一个需要考虑的问题是缺少滚动停止时的回调函数。这可能是也可能不是问题。您将与滚动同时移动焦点,而不是在末尾移动,这可能有点奇怪。无论如何,都需要做一些工作!

运动与无障碍性

有些人会因为屏幕上的快速移动而 感到恶心。我建议使用较慢的运动速度,因为如果 用户要跳过大量内容,如果速度过快,可能会导致眩晕感。

此外,为用户提供一种关闭动画的方法也不失为一个好主意。幸运的是,Safari 10.1 引入了 减少运动媒体查询,它为开发人员提供了一种以可通过浏览器级别禁用的方式包含动画的方法。

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

不幸的是,其他浏览器尚未实现此功能。因此,在支持范围扩大到不止一个浏览器之前,我们可以通过界面为用户提供一个选项来启用/禁用可能导致用户问题的动画。

<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()
      }
    })
  }
});

亲自尝试一下 此演示