溢出锚点不适用于水平滚动

overflow-anchor doesn't work for horizontal scrolling

我想构建一个可以向左右两个方向滚动的无限水平滚动条。当用户向左滚动时,新内容将添加到可滚动元素的前面(例如,考虑滚动日程历史记录)。当它们向右滚动时,会附加内容。

我了解到浏览器在上下滚动时会锚定内容,这太棒了,这正是我所期望的。这样做的效果是,将内容添加到滚动元素会将用户锚定到他们当前的逻辑位置,并且内容不会“跳转”。

但是向左或向右滚动时锚定似乎不起作用。行为就像我设置了 overflow-anchor: none。我该怎么做才能让它像向上滚动时一样工作?

let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
  if (event.target.scrollTop < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${topCounter--}`;
    document.querySelector('.scrollable-top').prepend(box);
  }
};

let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
  if (event.target.scrollLeft < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${leftCounter--}`;
    document.querySelector('.scrollable-left').prepend(box);
  }
};
.scrollable-top {
  width: 200px;
  height: 250px;
  overflow-y: auto;
  border: solid 1px black;
}

.scrollable-left {
  display: flex;
  width: 250px;
  overflow-x: auto;
  border: solid 1px black;
}

.content-box {
  flex: 1 0 auto;
  height: 150px;
  width: 150px;
  border: solid 1px red;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="scrollable-top">
  <div class="content-box">1</div>
  <div class="content-box">2</div>
  <div class="content-box">3</div>
</div>

<div class="scrollable-left">
  <div class="content-box">1</div>
  <div class="content-box">2</div>
  <div class="content-box">3</div>
</div>

我试图修复你的代码并得到了这个选项

let leftCounter = -2;
let rightCounter = 2;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
  if (event.target.scrollLeft < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${leftCounter--}`;
    document.querySelector('.scrollable-left').prepend(box);
    event.target.scrollLeft += 250
  }
  if ((event.target.scrollWidth - event.target.scrollLeft - 250) < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${rightCounter++}`;
    document.querySelector('.scrollable-left').append(box);
  }
};
.scrollable-top {
  width: 200px;
  height: 250px;
  overflow-y: auto;
  border: solid 1px black;
}

.scrollable-left {
  display: flex;
  width: 250px;
  overflow-x: auto;
  border: solid 1px black;
}

.content-box {
  flex: 1 0 auto;
  height: 150px;
  width: 150px;
  border: solid 1px red;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="scrollable-left">
  <div class="content-box">-1</div>
  <div class="content-box">0</div>
  <div class="content-box">1</div>
</div>

使用 scrollBy(150, 0) 将水平容器向右滚动 150:

let topCounter = 0;
document.querySelector('.scrollable-top').scrollTo({ top: 100 });
document.querySelector('.scrollable-top').onscroll = (event) => {
  if (event.target.scrollTop < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${topCounter--}`;
    document.querySelector('.scrollable-top').prepend(box);
  }
};

let leftCounter = 0;
document.querySelector('.scrollable-left').scrollTo({ left: 100 });
document.querySelector('.scrollable-left').onscroll = (event) => {
  if (event.target.scrollLeft < 100) {
    let box = document.createElement('div');
    box.className = 'content-box';
    box.textContent = `${leftCounter--}`;
    document.querySelector('.scrollable-left').prepend(box);
    
    // solution ------------------
    event.target.scrollBy(150, 0);
  }
};
.scrollable-top {
  width: 200px;
  height: 250px;
  overflow-y: auto;
  border: solid 1px black;
  display: inline-block;    
  float:left;
}

.scrollable-left {
  display: flex;
  width: 250px;
  overflow-x: auto;
  border: solid 1px black;
  float:right;
}

.content-box {
  flex: 1 0 auto;
  height: 150px;
  width: 150px;
  border: solid 1px red;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="scrollable-top">
  <div class="content-box">1</div>
  <div class="content-box">2</div>
  <div class="content-box">3</div>
</div>

<div class="scrollable-left">
  <div class="content-box">1</div>
  <div class="content-box">2</div>
  <div class="content-box">3</div>
</div>


根据 specs,以下是这种锚定背后的意图:

Changes in DOM elements above the visible region of a scrolling box can result in the page moving while the user is in the middle of consuming the content. This spec proposes a mechanism to mitigate this jarring user experience by keeping track of the position of an anchor node and adjusting the scroll offset accordingly.
This spec also proposes an API for web developers to opt-out of this behavior.

由于没有页面水平加载,我认为他们没有为水平滚动条实现这一点。此外,除了上述用例之外,实现此行为毫无意义。

注意: Safari doesn't implement overflow-anchor 行为。因此,您的垂直滚动代码在 Safari 中失败。
我已经在 Safari 上尝试了我的水平滚动代码并且它有效。因此,如果您想实现无限垂直滚动,并希望支持所有浏览器,那么您必须选择退出 overflow-anchor 行为并使用 scrollBy(x,y) 手动完成。 :(