jacoballenwood
jacoballenwood

Reputation: 3057

JavaScript - Scroll overflowed list element into view

So you have a long scrollable list, and want to scroll a specific DOM element into view:

I have have come up with a solution that I wanted to share with you guys, and I hope it is helpful to some! (this QA idea is taken directly from SO) I've taken some smaller JS tricks I've seen on SO and come up with the function down below:

Upvotes: 2

Views: 620

Answers (1)

jacoballenwood
jacoballenwood

Reputation: 3057

 /**
 * scroll an overflowed list so that the elementToView is visible
 *
 * @param      {<NodeList item>}   elementToView     list element to make visible
 * @param      {<NodeList item>}   elementContainer  element containing the elementToView
 * @param      {<NodeList item>}   listElement       actual list (<ul>?) element to scroll (could be the same as container)
 *
 *
 */
function scrollList(elementToView, elementContainer, listElement) {
    let containerHeight, topPos, listItem, temp = null
    if (typeof window !== 'undefined' && elementToView && elementContainer && listElement) {
        temp = window.getComputedStyle(elementContainer, null).getPropertyValue('padding-top')
        temp = parseInt(temp, 10) + parseInt(window.getComputedStyle(elementContainer, null).getPropertyValue('padding-bottom'), 10)
        // temp holds the top and bottom padding
        // usable space is the offsetHeight - padding
        containerHeight = elementContainer.offsetHeight - temp
        // amount of pixels away from the top of the container
        topPos = elementToView.offsetTop
        if (!containerHeight || !topPos) return false
        // get the height of a list item in the list to scroll 
        listItem = listElement.querySelector('li:first-of-type').offsetHeight
        // actually do the scrolling now
        // scroll the top of the list to show the elementToView on the bottom (as the last visible element in the list)
        listElement.scrollTop = (topPos - (containerHeight - listItem))
    }
}

Here is an example use of the function:

let focusElement = document.getElementsByClassName('focus')[0]
let containerElement = document.getElementsByClassName('modal')[0]
let listElement = document.getElementsByClassName('chapter-list')[0]
// let's check if any selection index has changed, and then scroll to the correct
// positions to make sure the selected elements are in view
if (chapterlistSelectionIndex !== prevState.chapterlistSelectionIndex) {
    scrollList(focusElement, containerElement, listElement)
}

This is going to get the DOM element (li item) with the focus class and scroll it to the last visible position in the chapter-list (meaning the position right above the bottom of the modal container)

Here are a few things I want to point out:

  • getComputedStyle (along with getPropertyValue) allows us to figure out how much actual useable space is in the container for scrolling, by getting rid of the padding calculated with offsetHeight
  • the second param to parseInt is important, and something I just discovered–here it makes sure the result is in base 10 (or NaN if it couldn't be parsed)
  • querySelector grabs the first child element that matches the css selector–in this case I'm using it to measure the visual height of an individual list item to ensure that the elementToView fits perfectly as the last visible list item
  • finally, we're going to scroll the elementToView to the distance of the container minus a list item from the top of the list (so that the list item is scrolled to look attached to the bottom of the container). this is where you could adjust if you want the item on the top of the list, or perhaps in the middle
  • also, just in case you're not sure about the let keyword, see this

I hope this helps!

Upvotes: 1

Related Questions