1

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:

Community
  • 1
  • 1
jacoballenwood
  • 2,787
  • 2
  • 24
  • 39
  • by what event on what element?..yes you can do that...but how? Is it a click on a button, a hover on a div, a mouse enter event on a form?....what are you trying to accomplish..have you tried anything and have issues with?... – repzero Oct 22 '16 at 01:55
  • @repzero sorry for the confusion–I had asked myself the same question and come up with a solution that works nicely for me, so I've answered my own question to hopefully help others – jacoballenwood Oct 22 '16 at 01:59
  • I'm voting to close this question as off-topic because SO is not a quiz-hosting site. – High Performance Mark Oct 22 '16 at 11:56
  • @HighPerformanceMark this is directly from SO: 'If you have a question that you already know the answer to, and you would like to document that knowledge in public so that others (including yourself) can find it later, it's perfectly okay to ask and answer your own question on a Stack Exchange site. To encourage people to do this, there is a checkbox at the bottom of the page every time you ask a question.' – jacoballenwood Oct 22 '16 at 14:12
  • from: http://stackoverflow.com/help/self-answer – jacoballenwood Oct 22 '16 at 14:12

1 Answers1

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

Community
  • 1
  • 1
jacoballenwood
  • 2,787
  • 2
  • 24
  • 39
  • 1
    You should have posted this couple months ago :) I have solved the similar problem my way, but your solution is more elegant and doesn't require jQuery :) – Morpheus Oct 22 '16 at 17:33
  • @Morpheus thanks for the feedback! :) yeah my project wasn't using jQuery, so I needed a vanilla solution! glad you got a working solution as well! – jacoballenwood Oct 22 '16 at 19:59