Javascript scrollIntoView() middle alignment?
Asked Answered
H

10

147

Javascript .scrollIntoView(boolean) provide only two alignment option.

  1. top
  2. bottom

What if I want to scroll the view such that. I want to bring particular element somewhere in middle of the page?

Handle answered 19/1, 2012 at 6:50 Comment(0)
A
55

Use window.scrollTo() for this. Get the top of the element you want to move to, and subtract one half the window height.

Demo: http://jsfiddle.net/ThinkingStiff/MJ69d/

Element.prototype.documentOffsetTop = function () {
    return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop() : 0 );
};

var top = document.getElementById( 'middle' ).documentOffsetTop() - ( window.innerHeight / 2 );
window.scrollTo( 0, top );
Abernathy answered 19/1, 2012 at 9:53 Comment(3)
It's a bit inefficient to walk up the whole parent chainScanner
i like the offsetTop - (container / 2). simple, but i didn't think of it.Nodababus
this didn't work inside a WebView, so I settled for a fake URI with #target on the end and no centeringIrmine
P
227

try this :

 document.getElementById('myID').scrollIntoView({
            behavior: 'auto',
            block: 'center',
            inline: 'center'
        });

refer here for more information and options : https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

Painless answered 21/5, 2018 at 17:31 Comment(10)
in combination with the polyfill, this is the best answer. npmjs.com/package/smoothscroll-polyfillFaubert
Only Chrome and Opera fully support block and inline, not even the smoothscroll polyfill does. It's the solution we want, but sadly not the one that works.Fluorescein
I am using the same solution in my project on IE11, Firefox, Chrome and Safari with Angular 5.Painless
@Sri7 Safari, IE and Edge do not understand the object-option of scrollIntoView and do whatever they want. Even on latest versions; I just tested it again.Fluorescein
I can confirm this does not work on mobile safari as of today. window.scrollBy works.Blackett
there is also a scrollIntoViewIfNeeded(true) if your browser supports developer.mozilla.org/en-US/docs/Web/API/Element/…Surfboard
This is the best solution for ChromeCerargyrite
@Faubert The smoothscroll-polyfill package seems abandoned and does not support block and inline parameters. Use this instead: npmjs.com/package/seamless-scroll-polyfillMuscolo
Working in an Angular component... With inline: 'center' the right-hand side moved inward, messing everything up, but inline: 'end' fixed that and now it's centering with block: 'center'.Belinda
2022. as of today. it works on Safari on desktop.Adjectival
V
90

It is possible to use getBoundingClientRect() to get all the information you need to achieve this. For example, you could do something like this:

const element = document.getElementById('middle');
const elementRect = element.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset;
const middle = absoluteElementTop - (window.innerHeight / 2);
window.scrollTo(0, middle);

Demo: http://jsfiddle.net/cxe73c22/

This solution is more efficient than walking up parent chain, as in the accepted answer, and doesn't involve polluting the global scope by extending prototype (generally considered bad practice in javascript).

The getBoundingClientRect() method is supported in all modern browsers.

Vivi answered 8/4, 2016 at 12:12 Comment(4)
Sadly, this breaks with absolute positioned elements.Fluorescein
Can you edit your answer to include how to do it when the element is in a scrollable container instead of the page? I tried: jsfiddle.net/qwxvts4u/1Cabot
Solved with the help of @KevinB jsfiddle.net/5ce062tnCabot
This solution worked for me with fixed headerDolt
A
55

Use window.scrollTo() for this. Get the top of the element you want to move to, and subtract one half the window height.

Demo: http://jsfiddle.net/ThinkingStiff/MJ69d/

Element.prototype.documentOffsetTop = function () {
    return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop() : 0 );
};

var top = document.getElementById( 'middle' ).documentOffsetTop() - ( window.innerHeight / 2 );
window.scrollTo( 0, top );
Abernathy answered 19/1, 2012 at 9:53 Comment(3)
It's a bit inefficient to walk up the whole parent chainScanner
i like the offsetTop - (container / 2). simple, but i didn't think of it.Nodababus
this didn't work inside a WebView, so I settled for a fake URI with #target on the end and no centeringIrmine
A
23
document.getElementById("id").scrollIntoView({block: "center"});
Acreinch answered 22/3, 2020 at 2:36 Comment(1)
I needed this for a vertically scrolling table with fixed header and this is working for me.Dewittdewlap
R
15

Scrolling to the middle of an element works well if its parent element has the css: overflow: scroll;

If it's a vertical list, you can use document.getElementById("id").scrollIntoView({block: "center"}); and it will scroll your selected element to the vertical middle of the parent element.

Cheers to Gregory R. and Hakuna for their good answers.

Further Reading:

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

https://developer.mozilla.org/en-US/docs/Web/CSS/overflow

Rodge answered 21/4, 2020 at 20:43 Comment(0)
A
10

You can do it in two steps :

myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, -viewportH/2); // Adjust scrolling with a negative value here

You can add the height of the element if you want to center it globaly, and not center its top :

myElement.scrollIntoView(true);
var viewportH = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
window.scrollBy(0, (myElement.getBoundingClientRect().height-viewportH)/2);
Archipelago answered 5/2, 2017 at 20:33 Comment(2)
this is the best answer for year 2018. first use normal "scrollIntoView", because "center" option is broken in Firefox 36 to 58 , and options are not supported by IE, see this developer.mozilla.org/en-US/docs/Web/API/Element/… . then you can center the scroll by calculate half of the viewport and move the scroll there.Fitts
Thanks, I can confirm that this worked on KaiOS where the "center" value of scrollIntoView isn't supported.Runic
L
3

With JQuery I use this:

function scrollToMiddle(id) {

    var elem_position = $(id).offset().top;
    var window_height = $(window).height();
    var y = elem_position - window_height/2;

    window.scrollTo(0,y);

}

Example:

<div id="elemento1">Contenido</div>

<script>
    scrollToMiddle("#elemento1");
</script>
Lalitta answered 16/10, 2016 at 23:39 Comment(0)
F
3

None of the solutions on this page work when a container other than the window/document is scrolled. The getBoundingClientRect approach fails with absolute positioned elements.

In that case we need to determine the scrollable parent first and scroll it instead of the window. Here is a solution that works in all current browser versions and should even work with IE8 and friends. The trick is to scroll the element to the top of the container, so that we know exactly where it is, and then subtract half of the screen's height.

function getScrollParent(element, includeHidden, documentObj) {
    let style = getComputedStyle(element);
    const excludeStaticParent = style.position === 'absolute';
    const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;

    if (style.position === 'fixed') {
        return documentObj.body;
    }
    let parent = element.parentElement;
    while (parent) {
        style = getComputedStyle(parent);
        if (excludeStaticParent && style.position === 'static') {
            continue;
        }
        if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) {
            return parent;
        }
        parent = parent.parentElement;
    }

    return documentObj.body;
}

function scrollIntoViewCentered(element, windowObj = window, documentObj = document) {
    const parentElement = getScrollParent(element, false, documentObj);
    const viewportHeight = windowObj.innerHeight || 0;

    element.scrollIntoView(true);
    parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;

    // some browsers (like FireFox) sometimes bounce back after scrolling
    // re-apply before the user notices.
    window.setTimeout(() => {
        element.scrollIntoView(true);
        parentElement.scrollTop = parentElement.scrollTop - viewportHeight / 2;
    }, 0);
}
Fluorescein answered 28/8, 2018 at 7:6 Comment(0)
H
2

Improving the answer of @Rohan Orton to work for vertical and horizontal scroll.

The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.

var ele = $x("//a[.='Ask Question']");
console.log( ele );

scrollIntoView( ele[0] );

function scrollIntoView( element ) {
    var innerHeight_Half = (window.innerHeight >> 1); // Int value
                        // = (window.innerHeight / 2); // Float value
    console.log('innerHeight_Half : '+ innerHeight_Half);

    var elementRect = element.getBoundingClientRect();

    window.scrollBy( (elementRect.left >> 1), elementRect.top - innerHeight_Half);
}

Using Bitwise operator right shift to get int value after dividing.

console.log( 25 / 2 ); // 12.5
console.log( 25 >> 1 ); // 12
Haphtarah answered 4/12, 2017 at 12:49 Comment(0)
B
1

To support all options in scrollIntoViewOptions for all browsers it's better to use seamless-scroll-polyfill (https://www.npmjs.com/package/seamless-scroll-polyfill)

Worked for me.

Here is a link with explanation https://github.com/Financial-Times/polyfill-library/issues/657

Bussy answered 9/10, 2020 at 10:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.