如何滚动查看 DOM 范围?

How do I scrollIntoView a DOM Range?

提问人:user3840170 提问时间:11/4/2023 最后编辑:user3840170 更新时间:11/24/2023 访问量:439

问:

Element 接口提供了一个 scrollIntoView 方法,该方法设置所有祖先元素的滚动位置,以便给定元素在屏幕上可见。

如何使用 DOM 范围执行类似的操作,也就是说,滚动需要滚动的任何内容,以便该范围在屏幕上可见?

我可能可以忍受手动计算范围的边界框并找到要滚动的元素,但我不想以任何方式修改 DOM 的内容。

javascript dom js-scrollintoview

评论

13赞 zurgeg 11/24/2023
Meta 上正在讨论这个问题。在此处了解这会如何影响您的问题
4赞 user16217248 11/27/2023
注意:即使用户关于这个问题的行为不合适,也不一定意味着该问题没有显示出任何研究努力;不清楚或没有用。请根据内容而不是用户进行投票。

答:

12赞 Salman A 11/16/2023 #1

Range 有一个有用的方法,称为 getBoundingClientRect,它返回一个 DOMRect,用于描述区域相对于视口的位置。然后,可以使用 将元素滚动到视图中。一个非常简单的例子:window.scrollBy

document.querySelector("#scroll-demo").addEventListener("click", function() {
  let selection = window.getSelection();
  if (selection.rangeCount === 0) {
    return;
  }
  let range = selection.getRangeAt(0);
  let rect = range.getBoundingClientRect();
  // scroll to top if:
  // - some part of selection is above the viewport
  // - some part of selection is below the viewport
  if (rect.top < 0 || rect.bottom > document.documentElement.clientHeight) {
    window.scrollBy(0, rect.y);
  }
});
p {
  max-width: 30em;
}
p:nth-child(even) {
  background-color: #EEE;
}
#demos {
  position: fixed;
  right: 0;
  top: 0;
  padding: 1em;
  color: #FFF;
  background-color: #000A;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc aliquam ultricies quam, non porttitor diam iaculis vitae. Nulla rutrum erat et tortor aliquet bibendum. Ut suscipit sapien id accumsan rutrum. Quisque eleifend tempor dolor sit amet luctus.
  Quisque non malesuada tortor. Nam sapien orci, consectetur dignissim magna eget, cursus gravida ligula. Quisque efficitur ornare felis, et venenatis ipsum laoreet vel. Sed nunc risus, imperdiet a porttitor eget, auctor eget neque. Donec tincidunt a
  est nec sodales. Suspendisse pellentesque est at luctus interdum. Quisque justo orci, vestibulum eget sapien et, faucibus imperdiet turpis. Etiam vel volutpat orci. Nullam tellus elit, bibendum a nulla quis, porttitor euismod sem.</p>
<p>Fusce vehicula nulla quis iaculis commodo. In in efficitur urna. Quisque sem nisl, luctus id porta ac, pellentesque a tellus. Pellentesque lacinia nisl non neque bibendum accumsan. Praesent vitae facilisis dolor. Sed vel ipsum non mauris consequat aliquet.
  Pellentesque tellus purus, consequat ut erat convallis, pretium molestie justo.</p>
<p>Maecenas sit amet laoreet leo, vel consectetur purus. Nunc in rhoncus ex. Etiam ultricies mauris ac felis aliquam semper. Morbi eleifend, justo a tincidunt pellentesque, est erat tincidunt nisi, tincidunt dictum nisi ipsum id nunc. Aliquam vel libero
  ipsum. In hac habitasse platea dictumst. Curabitur rhoncus risus in ultricies tempus. Vivamus elementum augue ac elit facilisis interdum. Duis tincidunt eget tortor posuere facilisis. Suspendisse fringilla, purus et faucibus tempus, dui elit viverra
  arcu, et iaculis massa justo eget ante. Nunc maximus gravida nulla ac tristique. Etiam vel tellus erat.</p>
<p>Nunc vitae risus dignissim, ultricies turpis at, bibendum erat. Mauris at ullamcorper lorem, eget posuere arcu. Aliquam quis libero turpis. Nullam maximus nisl enim, quis pellentesque lacus efficitur non. Fusce consectetur, erat vel elementum porttitor,
  risus libero vulputate risus, at porta ante lorem nec lorem. Sed vitae dolor in nisl tincidunt dapibus. Aliquam id velit sit amet ipsum pulvinar condimentum ut quis massa. Aenean id urna a nunc efficitur pulvinar. Ut sed hendrerit lectus. Sed fringilla
  urna eu nibh egestas, ac euismod sem hendrerit. Aliquam et risus accumsan, congue leo sed, ornare lacus. Nunc luctus erat odio, ut ultricies dui efficitur ut.</p>
<p>Praesent dapibus est nulla. Cras posuere, risus vel molestie porttitor, ipsum sapien pretium dolor, vitae bibendum arcu massa eget velit. Nunc laoreet lacinia ligula, vel ullamcorper est suscipit finibus. Sed sagittis ipsum vitae odio faucibus porta.
  In hac habitasse platea dictumst. Cras vitae consectetur nisl. Ut id arcu condimentum, auctor erat quis, volutpat ligula. Nulla vitae diam at odio dapibus auctor id sed lectus. Integer ac pharetra nisi, ac tincidunt dolor. Cras nec massa et purus dictum
  faucibus eget sed nisi. Phasellus interdum urna nec sem lobortis, non condimentum erat porta. Pellentesque sodales sit amet erat ut vulputate. Sed laoreet quis eros quis mollis. Pellentesque scelerisque mauris ornare interdum commodo. Nunc id rhoncus
  purus. Nullam sed enim nec ex dignissim pretium.</p>
<div id="demos">
  Select something in the document<br>
  then scroll it outside the viewport<br>
  <button id="scroll-demo">Scroll selection into view</button>
</div>

如果范围存在于嵌套的可滚动容器中,则情况会变得复杂。一个棘手的解决方案是将“最近的滚动父级”滚动到视图中,并将其与视口的顶部对齐,然后使用上述逻辑将范围滚动到视图中(边界矩形将为您提供相对于视口的位置,但此时所有滚动父项都与视口的顶部对齐,因此它应该可以正常工作)。

对于简单的用例,一个简单的解决方案是使用 Range.commonAncestorContainer 并使用 Element.scrollIntoView 标识包含范围的元素:

document.querySelector("#scroll-demo").addEventListener("click", function() {
  let selection = window.getSelection();
  if (selection.rangeCount === 0) {
    return;
  }
  let range = selection.getRangeAt(0);
  let element = range.commonAncestorContainer;
  if (element.nodeType === Node.TEXT_NODE) {
    element = element.parentElement;
  }
  element.scrollIntoView();
});
p {
  max-width: 30em;
}
p:nth-child(even) {
  background-color: #EEE;
}
#scrollable {
  max-width: 30em;
  height: 20em;
  overflow: auto;
  box-shadow: 0 0 4px 2px #F00;
}
#demos {
  position: fixed;
  right: 0;
  top: 0;
  padding: 1em;
  color: #FFF;
  background-color: #000A;
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc aliquam ultricies quam, non porttitor diam iaculis vitae. Nulla rutrum erat et tortor aliquet bibendum. Ut suscipit sapien id accumsan rutrum. Quisque eleifend tempor dolor sit amet luctus.
  Quisque non malesuada tortor. Nam sapien orci, consectetur dignissim magna eget, cursus gravida ligula. Quisque efficitur ornare felis, et venenatis ipsum laoreet vel. Sed nunc risus, imperdiet a porttitor eget, auctor eget neque. Donec tincidunt a
  est nec sodales. Suspendisse pellentesque est at luctus interdum. Quisque justo orci, vestibulum eget sapien et, faucibus imperdiet turpis. Etiam vel volutpat orci. Nullam tellus elit, bibendum a nulla quis, porttitor euismod sem.</p>
<p>Fusce vehicula nulla quis iaculis commodo. In in efficitur urna. Quisque sem nisl, luctus id porta ac, pellentesque a tellus. Pellentesque lacinia nisl non neque bibendum accumsan. Praesent vitae facilisis dolor. Sed vel ipsum non mauris consequat aliquet.
  Pellentesque tellus purus, consequat ut erat convallis, pretium molestie justo.</p>
<p>Maecenas sit amet laoreet leo, vel consectetur purus. Nunc in rhoncus ex. Etiam ultricies mauris ac felis aliquam semper. Morbi eleifend, justo a tincidunt pellentesque, est erat tincidunt nisi, tincidunt dictum nisi ipsum id nunc. Aliquam vel libero
  ipsum. In hac habitasse platea dictumst. Curabitur rhoncus risus in ultricies tempus. Vivamus elementum augue ac elit facilisis interdum. Duis tincidunt eget tortor posuere facilisis. Suspendisse fringilla, purus et faucibus tempus, dui elit viverra
  arcu, et iaculis massa justo eget ante. Nunc maximus gravida nulla ac tristique. Etiam vel tellus erat.</p>
<div id="scrollable">
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc aliquam ultricies quam, non porttitor diam iaculis vitae. Nulla rutrum erat et tortor aliquet bibendum. Ut suscipit sapien id accumsan rutrum. Quisque eleifend tempor dolor sit amet luctus.
    Quisque non malesuada tortor. Nam sapien orci, consectetur dignissim magna eget, cursus gravida ligula. Quisque efficitur ornare felis, et venenatis ipsum laoreet vel. Sed nunc risus, imperdiet a porttitor eget, auctor eget neque. Donec tincidunt
    a est nec sodales. Suspendisse pellentesque est at luctus interdum. Quisque justo orci, vestibulum eget sapien et, faucibus imperdiet turpis. Etiam vel volutpat orci. Nullam tellus elit, bibendum a nulla quis, porttitor euismod sem.</p>
  <p>Fusce vehicula nulla quis iaculis commodo. In in efficitur urna. Quisque sem nisl, luctus id porta ac, pellentesque a tellus. Pellentesque lacinia nisl non neque bibendum accumsan. Praesent vitae facilisis dolor. Sed vel ipsum non mauris consequat aliquet.
    Pellentesque tellus purus, consequat ut erat convallis, pretium molestie justo.</p>
  <p>Maecenas sit amet laoreet leo, vel consectetur purus. Nunc in rhoncus ex. Etiam ultricies mauris ac felis aliquam semper. Morbi eleifend, justo a tincidunt pellentesque, est erat tincidunt nisi, tincidunt dictum nisi ipsum id nunc. Aliquam vel libero
    ipsum. In hac habitasse platea dictumst. Curabitur rhoncus risus in ultricies tempus. Vivamus elementum augue ac elit facilisis interdum. Duis tincidunt eget tortor posuere facilisis. Suspendisse fringilla, purus et faucibus tempus, dui elit viverra
    arcu, et iaculis massa justo eget ante. Nunc maximus gravida nulla ac tristique. Etiam vel tellus erat.</p>
  <p>Nunc vitae risus dignissim, ultricies turpis at, bibendum erat. Mauris at ullamcorper lorem, eget posuere arcu. Aliquam quis libero turpis. Nullam maximus nisl enim, quis pellentesque lacus efficitur non. Fusce consectetur, erat vel elementum porttitor,
    risus libero vulputate risus, at porta ante lorem nec lorem. Sed vitae dolor in nisl tincidunt dapibus. Aliquam id velit sit amet ipsum pulvinar condimentum ut quis massa. Aenean id urna a nunc efficitur pulvinar. Ut sed hendrerit lectus. Sed fringilla
    urna eu nibh egestas, ac euismod sem hendrerit. Aliquam et risus accumsan, congue leo sed, ornare lacus. Nunc luctus erat odio, ut ultricies dui efficitur ut.</p>
  <p>Praesent dapibus est nulla. Cras posuere, risus vel molestie porttitor, ipsum sapien pretium dolor, vitae bibendum arcu massa eget velit. Nunc laoreet lacinia ligula, vel ullamcorper est suscipit finibus. Sed sagittis ipsum vitae odio faucibus porta.
    In hac habitasse platea dictumst. Cras vitae consectetur nisl. Ut id arcu condimentum, auctor erat quis, volutpat ligula. Nulla vitae diam at odio dapibus auctor id sed lectus. Integer ac pharetra nisi, ac tincidunt dolor. Cras nec massa et purus
    dictum faucibus eget sed nisi. Phasellus interdum urna nec sem lobortis, non condimentum erat porta. Pellentesque sodales sit amet erat ut vulputate. Sed laoreet quis eros quis mollis. Pellentesque scelerisque mauris ornare interdum commodo. Nunc
    id rhoncus purus. Nullam sed enim nec ex dignissim pretium.</p>
</div>
<p>Nunc vitae risus dignissim, ultricies turpis at, bibendum erat. Mauris at ullamcorper lorem, eget posuere arcu. Aliquam quis libero turpis. Nullam maximus nisl enim, quis pellentesque lacus efficitur non. Fusce consectetur, erat vel elementum porttitor,
  risus libero vulputate risus, at porta ante lorem nec lorem. Sed vitae dolor in nisl tincidunt dapibus. Aliquam id velit sit amet ipsum pulvinar condimentum ut quis massa. Aenean id urna a nunc efficitur pulvinar. Ut sed hendrerit lectus. Sed fringilla
  urna eu nibh egestas, ac euismod sem hendrerit. Aliquam et risus accumsan, congue leo sed, ornare lacus. Nunc luctus erat odio, ut ultricies dui efficitur ut.</p>
<p>Praesent dapibus est nulla. Cras posuere, risus vel molestie porttitor, ipsum sapien pretium dolor, vitae bibendum arcu massa eget velit. Nunc laoreet lacinia ligula, vel ullamcorper est suscipit finibus. Sed sagittis ipsum vitae odio faucibus porta.
  In hac habitasse platea dictumst. Cras vitae consectetur nisl. Ut id arcu condimentum, auctor erat quis, volutpat ligula. Nulla vitae diam at odio dapibus auctor id sed lectus. Integer ac pharetra nisi, ac tincidunt dolor. Cras nec massa et purus dictum
  faucibus eget sed nisi. Phasellus interdum urna nec sem lobortis, non condimentum erat porta. Pellentesque sodales sit amet erat ut vulputate. Sed laoreet quis eros quis mollis. Pellentesque scelerisque mauris ornare interdum commodo. Nunc id rhoncus
  purus. Nullam sed enim nec ex dignissim pretium.</p>
<div id="demos">
  Select something in the document<br>
  then scroll it outside the viewport<br>
  <button id="scroll-demo">Scroll selection into view</button>
</div>

评论

1赞 user3840170 11/17/2023
如果范围位于不是顶级窗口的可滚动元素中,这是否也有效?
0赞 Salman A 11/19/2023
很遗憾,不可以。我能想到的一个快速解决方案是用于定位完全包含范围的元素。如果得到一个元素:继续,如果你得到一个文本节点:find parentElement。然后打电话,希望浏览器完成剩下的工作。Range.commonAncestorContainerthatElement.scrollIntoView()
3赞 the Hutt 11/20/2023 #2

将范围中的第一个元素带到视口的顶部边框也可以解决该问题:

range.startContainer.scrollIntoView(true);

评论

0赞 user3840170 11/22/2023
当然,请确保至少有一部分范围是可见的;这与对元素(也可以大于视口)执行相同的操作没有太大区别。