/**
* 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!