251

I am using ScrollIntoView() to scroll the highlighted item in a list into view.
When I scroll downwards ScrollIntoView(false) works perfectly.

But when I scroll upwards, ScrollIntoView(true) is causing the whole page to move a little which I think is intended.

Is there a way to avoid the whole page move when using ScrollIntoView(true)?

Here is the structure of my page

#listOfDivs {
   position:fixed;
   top:100px;
   width: 300px;
   height: 300px;
   overflow-y: scroll;
}

<div id="container">
     <div id="content"> 
          <div id="listOfDivs"> 
               <div id="item1"> </div>
               <div id="item2"> </div>
               <div id="itemn"> </div>
          </div>
     </div>
</div>

listOfDivs is coming from ajax call. Using mobile safari.

3
  • 1
    Can you provide a JSFiddle so we can see exactly what the problem is? Commented Jun 15, 2012 at 6:58
  • Simple reproduction in case it's useful to others: codesandbox.io/s/frosty-wave-g2y5d Commented Apr 1, 2021 at 12:27
  • Also you may wish to update the answer to @jfrohn's Commented Apr 1, 2021 at 12:28

17 Answers 17

346

Fixed it with:

element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })

see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

17
  • 15
    What does "nearest" mean? EDIT: Found an answer here: stackoverflow.com/a/48635751/628418 Commented Jul 10, 2019 at 13:52
  • 4
    This resolved my issue. I've been seeing this happen in our web app for some time, but it's been hard to lock down the repro for it. After adding a new feature it occurred every time and I was able to trace it to this call, which was missing the block : 'nearest' param.
    – roskelld
    Commented Jul 18, 2019 at 20:47
  • 2
    Works with Chrome, but not with Edge for me.
    – Michael
    Commented Oct 8, 2019 at 15:18
  • 14
    "block: 'nearest'" works, but "block: 'center'" (which is a bit nicer) still scrolls the viewport. It would be nice to know whether this is a bug or a feature...
    – GrumpyGary
    Commented Jan 17, 2020 at 18:46
  • 5
    @som I had a similar issue where my whole page was offset after using scrollIntoView() and the thing that prevented this for me in the end was overflow: clip; on the main element. Many thanks! Commented Dec 8, 2022 at 20:14
184

You could use scrollTop instead of scrollIntoView():

var target = document.getElementById("target");
target.parentNode.scrollTop = target.offsetTop;

jsFiddle: http://jsfiddle.net/LEqjm/

If there's more than one scrollable element that you want to scroll, you'll need to change the scrollTop of each one individually, based on the offsetTops of the intervening elements. This should give you the fine-grained control to avoid the problem you're having.

EDIT: offsetTop isn't necessarily relative to the parent element - it's relative to the first positioned ancestor. If the parent element isn't positioned (relative, absolute or fixed), you may need to change the second line to:

target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop;
13
  • 7
    FYI, this only works when the parent is at the top of the page. You should probably make it target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop
    – Phil
    Commented Mar 20, 2013 at 23:56
  • 3
    offsetTop is relative to the nearest positioned parent, so if the parent node has position: relative, then you shouldn't subtract its offset. But you're right for most cases.
    – Brilliand
    Commented Mar 22, 2013 at 20:01
  • 4
    Here is a js fiddle: jsfiddle.net/LEqjm/258 that shows also the ScrollIntoView improper behavior.
    – Rafi
    Commented Sep 1, 2016 at 12:37
  • 1
    @Brilliand thank you for idea about position: relative on prent div: it really fix the problem Commented Dec 7, 2016 at 15:49
  • It is working on chrome but it scrolls little bit more on IE, which hides the text content from the top. Any solution @Phil, Thanks in advance
    – Soniya
    Commented Jun 8, 2018 at 4:13
24
var el = document.querySelector("yourElement");
window.scroll({top: el.offsetTop, behavior: 'smooth'});
2
  • or you can use top: el['offsetTop'] Commented Aug 29, 2018 at 15:29
  • This works the best for me. But still a little buggy on firefox - sometimes it stops part way to the element. Fine on edge and chrome.
    – Mark
    Commented Aug 16, 2020 at 5:58
23

I had this problem too, and spent many hours trying to deal with it. I hope my resolution may still help some people.

My fix ended up being:

  • For Chrome: changing .scrollIntoView() to .scrollIntoView({block: 'nearest'}) (thanks to @jfrohn).
  • For Firefox: apply overflow: -moz-hidden-unscrollable; on the container element that shifts.
  • Not tested in other browsers.
1
  • 6
    Looks like overflow: clip is the new -moz-hidden-unscrollable
    – som
    Commented Mar 19, 2021 at 5:46
16

in my context, he would push the sticky toolbar off the screen, or enter next to a fab button with absolute.

using the nearest solved.

const element = this.element.nativeElement;
const table = element.querySelector('.table-container');
table.scrollIntoView({
  behavior: 'smooth', block: 'nearest'
});
1
  • nearest worked for me with Chrome 121, Firefox 123. Commented Feb 21 at 15:14
15

Play around with scrollIntoViewIfNeeded() ... make sure it's supported by the browser.

2
  • 1
    Didn't know that was a thing. There's a polyfill
    – mpen
    Commented Mar 11, 2016 at 19:47
  • In document, its mentioned there that it is non standard but it's working fine. should we use it? Commented Mar 7, 2019 at 13:05
10

I've added a way to display the imporper behavior of the ScrollIntoView - http://jsfiddle.net/LEqjm/258/ [it should be a comment but I don't have enough reputation]

$("ul").click(function() {
    var target = document.getElementById("target");
    if ($('#scrollTop').attr('checked')) {
        target.parentNode.scrollTop = target.offsetTop;    
    } else {
        target.scrollIntoView(!0);
    }
});
0
9

Just to add an answer as per my latest experience and working on VueJs. I found below piece of code ad best, which does not impact your application in anyways.

const el = this.$el.getElementsByClassName('your_element_class')[0];
if (el) {
   scrollIntoView(el,
                  {
                       block: 'nearest',
                       inline: 'start',
                       behavior: 'smooth',
                       boundary: document.getElementsByClassName('main_app_class')[0]
                    });
     }

main_app_class is the root class

your_element_class is the element/view where you can to scroll into

And for browser which does not support ScrollIntoView() just use below library its awesome https://www.npmjs.com/package/scroll-into-view-if-needed

5
  • 4
    The 'boundary' is not included in a scollIntoView specification. Any ideas?
    – Joe
    Commented Mar 16, 2021 at 8:30
  • @Joe Better you use this library https://www.npmjs.com/package/scroll-into-view-if-needed instead of native one Commented Mar 16, 2021 at 8:46
  • Something like this shouldn't require a library
    – Kalnode
    Commented Nov 23, 2021 at 15:15
  • @MarsAndBack the native scrollIntoView does not support some legacy browsers and it fails in safari most of the times, so best way is to make the app bulletproof by using above library Commented Nov 24, 2021 at 7:19
  • 'boundary' property was really useful, to avoid scrolling the whole page, but just the list. Thanks Commented Jul 14, 2023 at 6:20
8

jQuery plugin scrollintoview() increases usability

Instead of default DOM implementation you can use a plugin that animates movement and doesn't have any unwanted effects. Here's the simplest way of using it with defaults:

$("yourTargetLiSelector").scrollintoview();

Anyway head over to this blog post where you can read all the details and will eventually get you to GitHub source codeof the plugin.

This plugin automatically searches for the closest scrollable ancestor element and scrolls it so that selected element is inside its visible view port. If the element is already in the view port it doesn't do anything of course.

3
  • 20
    @Brilliand: That is of course true, but that doesn't mean that they're not using it or are reluctant to using it. All I'm doing is providing an alternative. It's on OP to decide whether that's a path they'd take or not. Maybe they never considered using jQuery in the first place. Commented Jun 21, 2012 at 8:32
  • That OP didn't specifically mention jQuery is utterly irrelevant: @RobertKoritnik's answer served me perfectly.
    – Dave Land
    Commented Sep 2, 2018 at 20:35
  • 1
    Incidentally, Bing is showing your code sample in the search results for "keep ancestor element from scrolling with scrollintoview." :-)
    – Dave Land
    Commented Sep 2, 2018 at 21:24
8

Adding more information to @Jesco post.

  • Element.scrollIntoViewIfNeeded() non-standard WebKit method for Chrome, Opera, Safari browsers.
    If the element is already within the visible area of the browser window, then no scrolling takes place.
  • Element.scrollIntoView() method scrolls the element on which it's called into the visible area of the browser window.

Try the below code in mozilla.org scrollIntoView() link. Post to identify Browser

var xpath = '//*[@id="Notes"]';
ScrollToElement(xpath);

function ScrollToElement(xpath) {
    var ele = $x(xpath)[0];
    console.log( ele );

    var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
    if (isChrome) { // Chrome
        ele.scrollIntoViewIfNeeded();
    } else {
        var inlineCenter = { behavior: 'smooth', block: 'center', inline: 'start' };
        ele.scrollIntoView(inlineCenter);
    }
}
5

FWIW: I found (in Chrome 95, and Firefox 92 (all Mac)) that using:

.scrollIntoView({ behavior:'smooth', block:'center'});

on a scrollable list of options would scroll the body element a little, so I opted to use:

.scrollIntoView({ behavior:'smooth', block:'nearest'});

and select an option past the one I wanted centered (e.g. in a scrollable elem with 5 lines/options viewable, I selected the 2nd option past the one I wanted centered, thereby centering the desired element.

enter image description here

4

I found (in Chrome) I could more reliably scroll my element to the top of my parent div (without moving the page) if I scrolled from the bottom up to my element rather than from the top down to my element. Otherwise while my element would scroll into view, it would sometimes still be lower than desired within the div.

To achieve this, I am scrolling in two steps:

  1. myScrollableDiv.scrollTop = myScrollableDiv.scrollHeight which instantly scrolls to the bottom of my scrollable div
  2. (as per other answers here) Scroll my the element into view with animation:
myElementWithinTheScrollingDiv.scrollIntoView({
  behavior: 'smooth',
  block: 'nearest',
})
2

Using Brilliant's idea, here's a solution that only (vertically) scrolls if the element is NOT currently visible. The idea is to get the bounding box of the viewport and the element to be displayed in browser-window coordinate space. Check if it's visible and if not, scroll by the required distance so the element is shown at the top or bottom of the viewport.

    function ensure_visible(element_id)
    {
        // adjust these two to match your HTML hierarchy
        var element_to_show  = document.getElementById(element_id);
        var scrolling_parent = element_to_show.parentElement;

        var top = parseInt(scrolling_parent.getBoundingClientRect().top);
        var bot = parseInt(scrolling_parent.getBoundingClientRect().bottom);

        var now_top = parseInt(element_to_show.getBoundingClientRect().top);
        var now_bot = parseInt(element_to_show.getBoundingClientRect().bottom);

        // console.log("Element: "+now_top+";"+(now_bot)+" Viewport:"+top+";"+(bot) );

        var scroll_by = 0;
        if(now_top < top)
            scroll_by = -(top - now_top);
        else if(now_bot > bot)
            scroll_by = now_bot - bot;
        if(scroll_by != 0)
        {
            scrolling_parent.scrollTop += scroll_by; // tr.offsetTop;
        }
    }
2

ScrollIntoView() causes page movement. But the following code works fine for me and move the screen to the top of the element:

window.scroll({
  top: document.getElementById('your-element')?.offsetParent.offsetTop,
  behavior: 'smooth',
  block: 'start',
})
0
1

i had the same problem, i fixed it by removing the transform:translateY CSS i placed on the footer of the page.

1

Spent too many hours on a similar issue, the only thing that worked was using overflow: clip (instead of hidden) on the container element that was scrolling too far.

Quoting from the MDN docs

clip: Overflow content is clipped at the element's overflow clip edge that is defined using the overflow-clip-margin property. As a result, content overflows the element's padding box by the value of overflow-clip-margin or by 0px if not set. Overflow content outside the clipped region is not visible, user agents do not add a scroll bar, and programmatic scrolling is also not supported. No new formatting context is created. To establish a formatting context, use overflow: clip along with display: flow-root. The element box is not a scroll container.

HT to this comment above for suggesting it.

0

I wrote this and solved my problem.

function scrollSmoothTargetElmToCenterOfparentElm(parentElm, targetElm) {
    const parentElmHalfOfHeight = parentElm.offsetHeight / 2
    const activeElmHalfOfHeight = targetElm.offsetHeight / 2
    const finalScrollPosition =
        targetElm.offsetTop - parentElm.offsetTop - parentElmHalfOfHeight + activeElmHalfOfHeight
    let currentScrollPosition = parentElm.scrollTop
    const INT = setInterval(function () {
        currentScrollPosition++
        parentElm.scrollTop = currentScrollPosition
        if (currentScrollPosition + 1 >= finalScrollPosition) clearInterval(INT)
    })
}

// sample of usage
const parentElm = document.getElementById("parent")
const targetElm = document.getElementById("target")
scrollSmoothTargetElmToCenterOfparentElm(parentElm, targetElm)

Not the answer you're looking for? Browse other questions tagged or ask your own question.