scrollIntoViewIfNeeded 的行为

Behavior of scrollIntoViewIfNeeded

我正在努力创建一个网络组件,给定一个可聚焦的元素列表,自动具有可访问的键盘导航。

其中一部分是让活动元素滚动到视图中,如果它还没有,为此我正在使用 Element.scrollIntoViewIfNeeded(在 chrome 中,所以应该支持'这不是问题)。

我遇到了一些奇怪的行为,我不太明白。我创建了一个最小代码沙箱来显示问题。

const app = document.getElementById("app");

for (var i = 0; i < 100; i++) {
  const paragraph = document.createElement("p");
  paragraph.innerHTML = "element " + i;
  paragraph.tabIndex = -1;
  app.appendChild(paragraph);
}

const handleKeyDown = (event) => {
  event.preventDefault();

  switch (event.key) {
    case "ArrowDown":
      if (app.contains(document.activeElement)) {
        const next = document.activeElement.nextElementSibling;
        next.focus();
        next.scrollIntoViewIfNeeded(false);
      } else {
        const first = app.firstElementChild;
        first.focus();
        first.scrollIntoViewIfNeeded(false);
      }
      break;
    case "ArrowUp":
      if (app.contains(document.activeElement)) {
        const previous = document.activeElement.previousElementSibling;
        previous.focus();
        previous.scrollIntoViewIfNeeded(false);
      } else {
        const last = app.lastElementChild;
        last.focus();
        last.scrollIntoViewIfNeeded(false);
      }
      break;
    default:
      break;
  }
};

document.addEventListener("keydown", handleKeyDown, false);
body {
  font-family: sans-serif;
}

.app {
  display: flex;
  flex-direction: column;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app" class="app"></div>

    <script src="src/index.js"></script>
  </body>
</html>

centerIfNeeded 论点似乎根本没有受到尊重。有时它会在我第一次关注视口之外的元素时受到尊重,但第二次不会,有时它会被完全忽略。

这是预期的行为吗?

问题是默认情况下 focus() 调用本身会在内部调用 scrollIntoView(),这两个调用会发生冲突。
为防止这种情况,您可以将 { preventScroll: true } 选项传递给对 focus().

的调用

const app = document.getElementById("app");

for (var i = 0; i < 100; i++) {
  const paragraph = document.createElement("p");
  paragraph.innerHTML = "element " + i;
  paragraph.tabIndex = -1;
  app.appendChild(paragraph);
}

const handleKeyDown = (event) => {
  event.preventDefault();

  switch (event.key) {
    case "ArrowDown":
      if (app.contains(document.activeElement)) {
        const next = document.activeElement.nextElementSibling;
        next?.focus({ preventScroll: true });
        next?.scrollIntoViewIfNeeded(false);
      } else {
        const first = app.firstElementChild;
        next?.focus({ preventScroll: true });
        first?.scrollIntoViewIfNeeded(false);
      }
      break;
    case "ArrowUp":
      if (app.contains(document.activeElement)) {
        const previous = document.activeElement.previousElementSibling;
        previous?.focus({ preventScroll: true });
        previous?.scrollIntoViewIfNeeded(false);
      } else {
        const last = app.lastElementChild;
        last?.focus({ preventScroll: true });
        last?.scrollIntoViewIfNeeded(false);
      }
      break;
    default:
      break;
  }
};

document.addEventListener("keydown", handleKeyDown, false);
body {
  font-family: sans-serif;
}

.app {
  display: flex;
  flex-direction: column;
}
<!DOCTYPE html>
<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app" class="app"></div>

    <script src="src/index.js"></script>
  </body>
</html>