How do I scrollIntoView a DOM Range?

974 views Asked by At

The Element interface offers a scrollIntoView method that sets the scroll positions of all ancestor elements so that the given element is visible on the screen.

How can I perform an analogous operation with a DOM Range, which is to say, scroll whatever it is that needs to be scrolled so that the range is visible on the screen?

I can probably live with manually computing the bounding box of the range and finding which element to scroll, but I do not wish to modify the contents of the DOM in any way.

2

There are 2 answers

2
Salman A On

Range has a useful method called getBoundingClientRect which returns a DOMRect describing the position of the range relative to the viewport. You can then scroll the element into view using window.scrollBy. A very simple example:

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>

It becomes complicated if the range is present inside nested scrollable containers. A tricky solution is to scroll the "nearest scroll parent" into view and align it with the top of the viewport, then use the above logic to scroll the range into view (the bounding rect will give you the position relative to the viewport but at this point all scroll parents are aligned to the top of the viewport so it should just work).

For simple use cases, a simple solution is to identify the element containing the range using Range.commonAncestorContainer and use 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
the Hutt On

Bringing the first element in the range to viewport's top border could also solve the problem:

range.startContainer.scrollIntoView(true);