How do I scroll to an element within an overflowed Div?

Jquery

Jquery Problem Overview


I have 20 list items inside of a div that can only show 5 at a time. What is a good way to scroll to item #10, and then item #20? I know the height of all the items.

The scrollTo plugin does this, but its source is not super easy to understand without really getting into it. I don't want to use this plugin.

Let's say I have a function that takes 2 elements $parentDiv, $innerListItem, neither $innerListItem.offset().top nor $innerListItem.positon().top gives me the correct scrollTop for $parentDiv.

Jquery Solutions


Solution 1 - Jquery

The $innerListItem.position().top is actually relative to the .scrollTop() of its first positioned ancestor. So the way to calculate the correct $parentDiv.scrollTop() value is to begin by making sure that $parentDiv is positioned. If it doesn't already have an explicit position, use position: relative. The elements $innerListItem and all its ancestors up to $parentDiv need to have no explicit position. Now you can scroll to the $innerListItem with:

// Scroll to the top
$parentDiv.scrollTop($parentDiv.scrollTop() + $innerListItem.position().top);

// Scroll to the center
$parentDiv.scrollTop($parentDiv.scrollTop() + $innerListItem.position().top
    - $parentDiv.height()/2 + $innerListItem.height()/2);

Solution 2 - Jquery

This is my own plugin (will position the element in top of the the list. Specially for overflow-y : auto. May not work with overflow-x!):

NOTE: elem is the HTML selector of an element which the page will be scrolled to. Anything supported by jQuery, like: #myid, div.myclass, $(jquery object), [dom object], etc.

jQuery.fn.scrollTo = function(elem, speed) { 
	$(this).animate({
		scrollTop:  $(this).scrollTop() - $(this).offset().top + $(elem).offset().top 
	}, speed == undefined ? 1000 : speed); 
    return this; 
};

If you don't need it to be animated, then use:

jQuery.fn.scrollTo = function(elem) { 
	$(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top); 
    return this; 
};

How to use:

$("#overflow_div").scrollTo("#innerItem");
$("#overflow_div").scrollTo("#innerItem", 2000); //custom animation speed 

Note: #innerItem can be anywhere inside #overflow_div. It doesn't really have to be a direct child.

Tested in Firefox (23) and Chrome (28).

If you want to scroll the whole page, check this question.

Solution 3 - Jquery

I've adjusted Glenn Moss' answer to account for the fact that overflow div might not be at the top of the page.

parentDiv.scrollTop(parentDiv.scrollTop() + (innerListItem.position().top - parentDiv.position().top) - (parentDiv.height()/2) + (innerListItem.height()/2)  )

I was using this on a google maps application with a responsive template. On resolution > 800px, the list was on the left side of the map. On resolution < 800 the list was below the map.

Solution 4 - Jquery

The above answers will position the inner element at the top of the overflow element even if it's in view inside the overflow element. I didn't want that so I modified it to not change the scroll position if the element is in view.

jQuery.fn.scrollTo = function(elem, speed) {
	var $this = jQuery(this);
	var $this_top = $this.offset().top;
	var $this_bottom = $this_top + $this.height();
	var $elem = jQuery(elem);
	var $elem_top = $elem.offset().top;
	var $elem_bottom = $elem_top + $elem.height();
	
	if ($elem_top > $this_top && $elem_bottom < $this_bottom) {
		// in view so don't do anything
		return;
	}
	var new_scroll_top;
	if ($elem_top < $this_top) {
		new_scroll_top = {scrollTop: $this.scrollTop() - $this_top + $elem_top};
	} else {
		new_scroll_top = {scrollTop: $elem_bottom - $this_bottom + $this.scrollTop()};
	}
    $this.animate(new_scroll_top, speed === undefined ? 100 : speed);
    return this;
};

Solution 5 - Jquery

The accepted answer only works with direct children of the scrollable element, while the other answers didn't centered the child in the scrollable element.

Example HTML:

<div class="scrollable-box">
    <div class="row">
        <div class="scrollable-item">
            Child 1
        </div>
        <div class="scrollable-item">
            Child 2
        </div>
        <div class="scrollable-item">
            Child 3
        </div>
        <div class="scrollable-item">
            Child 4
        </div>
        <div class="scrollable-item">
            Child 5
        </div>
        <div class="scrollable-item">
            Child 6
        </div>
        <div class="scrollable-item">
            Child 7
        </div>
        <div class="scrollable-item">
            Child 8
        </div>
        <div class="scrollable-item">
            Child 9
        </div>
        <div class="scrollable-item">
            Child 10
        </div>
        <div class="scrollable-item">
            Child 11
        </div>
        <div class="scrollable-item">
            Child 12
        </div>
        <div class="scrollable-item">
            Child 13
        </div>
        <div class="scrollable-item">
            Child 14
        </div>
        <div class="scrollable-item">
            Child 15
        </div>
        <div class="scrollable-item">
            Child 16
        </div>
    </div>
</div>

<style>
.scrollable-box {
    width: 800px;
    height: 150px;
    overflow-x: hidden;
    overflow-y: scroll;
    border: 1px solid #444;
}
.scrollable-item {
    font-size: 20px;
    padding: 10px;
    text-align: center;
}
</style>

Build a small jQuery plugin:

$.fn.scrollDivToElement = function(childSel) {
    if (! this.length) return this;

    return this.each(function() {
        let parentEl = $(this);
        let childEl = parentEl.find(childSel);

        if (childEl.length > 0) {
            parentEl.scrollTop(
                parentEl.scrollTop() - parentEl.offset().top + childEl.offset().top - (parentEl.outerHeight() / 2) + (childEl.outerHeight() / 2)
            );
        }
    });
};

Usage:

$('.scrollable-box').scrollDivToElement('.scrollable-item:eq(12)');

Explanation:

  1. parentEl.scrollTop(...) sets current vertical position of the scroll bar.

  2. parentEl.scrollTop() gets the current vertical position of the scroll bar.

  3. parentEl.offset().top gets the current coordinates of the parentEl relative to the document.

  4. childEl.offset().top gets the current coordinates of the childEl relative to the document.

  5. parentEl.outerHeight() / 2 gets the outer height divided in half (because we want it centered) of the child element.

  6. when used:

$(parent).scrollDivToElement(child);

The parent is the scrollable div and it can be a string or a jQuery object of the DOM element.
The child is any child that you want to scroll to and it can be a string or a jQuery object of the DOM element.

Codepen demo

Solution 6 - Jquery

I write these 2 functions to make my life easier:

function scrollToTop(elem, parent, speed) {
	var scrollOffset = parent.scrollTop() + elem.offset().top;
	parent.animate({scrollTop:scrollOffset}, speed);
	// parent.scrollTop(scrollOffset, speed);
}

function scrollToCenter(elem, parent, speed) {
	var elOffset = elem.offset().top;
	var elHeight = elem.height();
	var parentViewTop = parent.offset().top;
	var parentHeight = parent.innerHeight();
    var offset;

    if (elHeight >= parentHeight) {
        offset = elOffset;
    } else {
        margin = (parentHeight - elHeight)/2;
        offset = elOffset - margin;
	}

	var scrollOffset = parent.scrollTop() + offset - parentViewTop;

	parent.animate({scrollTop:scrollOffset}, speed);
	// parent.scrollTop(scrollOffset, speed);
}

And use them:

scrollToTop($innerListItem, $parentDiv, 200);
// or
scrollToCenter($innerListItem, $parentDiv, 200);

Solution 7 - Jquery

After playing with it for a very long time, this is what I came up with:

    jQuery.fn.scrollTo = function (elem) {
        var b = $(elem);
        this.scrollTop(b.position().top + b.height() - this.height());
    };

and I call it like this

$("#basketListGridHolder").scrollTo('tr[data-uid="' + basketID + '"]');

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmkoryakView Question on Stackoverflow
Solution 1 - JqueryGlenn MossView Answer on Stackoverflow
Solution 2 - JquerylepeView Answer on Stackoverflow
Solution 3 - JqueryKPheaseyView Answer on Stackoverflow
Solution 4 - JquerymikeView Answer on Stackoverflow
Solution 5 - JqueryBinar WebView Answer on Stackoverflow
Solution 6 - JquerytrankView Answer on Stackoverflow
Solution 7 - Jquerybruce reevesView Answer on Stackoverflow