Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IE 11 HTMLElement.scrollIntoView() doesn't execute #12

Open
enijar opened this issue Feb 26, 2020 · 6 comments
Open

IE 11 HTMLElement.scrollIntoView() doesn't execute #12

enijar opened this issue Feb 26, 2020 · 6 comments

Comments

@enijar
Copy link

enijar commented Feb 26, 2020

Firstly, great polyfill – it works perfectly for scrollTo, however I'm unable to get scrollIntoView working.

Environment

IE 11 on Windows 10

Expected behaviour

When I call HTMLElement.scrollIntoView, I expect the page to scroll to the element.

Current Behaviour

When I call HTMLElement.scrollIntoView nothing happens; no console errors, no scroll, nothing.

Implementation

I have a function which I call, that looks like this:

async function scrollToSection(section) {
    if (!('scrollBehavior' in document.documentElement.style)) {
        console.log('Loading scroll behaviour polyfill...');
        await import('scroll-behavior-polyfill');
    }
    const container = document.querySelector(`.Section--${section}`);
    container && container.scrollIntoView();
}

My scrolling element is a custom element (so not the html or body elements). Do you think this is what could be causing the issue?

As a work-around, I've done this:

async function scrollTo(scrollContainer, {x, y, behavior = 'smooth'}) {
    if (!('scrollBehavior' in document.documentElement.style)) {
        console.log('Loading scroll behaviour polyfill...');
        await import('scroll-behavior-polyfill');
    }
    scrollContainer.scrollTo({left: x, top: y, behavior});
}

function scrollToSection(section) {
    const scrollContainer = document.querySelector('#root-app');
    const container = document.querySelector(`.Section--${section}`);
    if (container) {
        const {top} = container.getBoundingClientRect();
        scrollTo(scrollContainer, {y: top});
    }
}
@wessberg
Copy link
Owner

Hey there. Are you attaching a shadow root to your scrolling element? In any case, there should be no issues since this polyfill is built with full Shadow DOM support. Is there a way for you to provide me with a simple reproduction of this problem that occurs so I can investigate it? scrollIntoView at least under some circumstances should work just fine in IE 11, so I'd like a repro to work from ☺️

@enijar
Copy link
Author

enijar commented Feb 26, 2020

I'm using this inside of a React-based application, so no shadow DOM, just the normal DOM of the page. I can confirm the element is mounted before I load the polyfill. I'm on mobile right now, but I'll share a CodeSandbox link when I'm on my home machine. Note that scrollTo works with this polyfill; it's only scrollIntoView that's not working as expected.

@wessberg
Copy link
Owner

Oh, alright. You mentioned it was a custom element, so I figured you might be using Shadow DOM. Anyway, that sounds great. I'll take a look at it when you have it. And thanks for reporting this bug!

@enijar
Copy link
Author

enijar commented Feb 26, 2020

Here is a reproducable error: https://codesandbox.io/s/crazy-hopper-nfp2l

Let me know if you can see the issue on IE11.

Edit: I just tried to load CodeSandbox in IE 11 on Browserstack and it doesn't work 😬

@SeinopSys
Copy link

SeinopSys commented Aug 11, 2020

Hello, I believe I ran into the same issue, this polyfill was included in a project where a non-functional scrollIntoView call was reported as a bug when using IE11, I suspect it might have to do with our specific layout, but I created an example barebones react app that reproduces the broken behavior. Due to none of the code editors I found online actually working in IE11 I'm 📎 attaching the app as a zip, extract it then run

npm install
npm install -g serve
npm build
serve -s build

If you open the URL the last command prints in current stable Chrome/Firefox and IE11 you can see that pressing the button to scroll to the element at the 500th index does nothing in IE while it works in other browsers. In my experience this is because an element in the list of parents actually appears scrollable, but when you set its scrollTop then try to look at it immediately after it remains unchanged at 0. Before I discovered this polyfill was already pulled in I wrote my own solution in TypeScript which seemed to do the trick, just trying to scroll all parents until one of them actually accepts the change. Hopefully this helps in finding a proper solution:

/**
 * This is a polyfill for the HTMLElement.scrollIntoView function for IE11
 * Based on https://github.com/tidal-engineering/ie11-scroll-into-view
 */
export const applyScrollIntoViewPolyfill = () => {
    const isIE = typeof window !== 'undefined' && window.navigator.userAgent.indexOf('Trident/') > 0;

    if (!isIE) return;

    const getComputedStyle = (el: HTMLElement): CSSStyleDeclaration => window.getComputedStyle(el, null);
    const overflowScrollable = (text: string) => text === 'scroll' || text === 'auto' || text === 'visible';
    const isXScrollable = (elem: HTMLElement) => (
        elem.clientWidth < elem.scrollWidth
        && overflowScrollable(getComputedStyle(elem).overflowX)
    );
    const isYScrollable = (elem: HTMLElement) => (
        elem.clientHeight < elem.scrollHeight
        && overflowScrollable(getComputedStyle(elem).overflowY)
    );
    const hasScrollbar = (elem: HTMLElement) => isYScrollable(elem) || isXScrollable(elem);
    const offsetTop = (el: HTMLElement) => el.getBoundingClientRect().top;

    /** @return A list of parent elements that are likely to be scrollable */
    function findScrollableParents(el: HTMLElement): HTMLElement[] {
        let checkElement: HTMLElement = el;
        const scrollableParents = [];

        while (checkElement !== null) {
            if (hasScrollbar(checkElement))
                scrollableParents.push(checkElement);

            checkElement = checkElement.parentNode as HTMLElement;
        }

        return scrollableParents;
    }

    /**
     * Turns out IE11 just "gives up" when the native scrollIntoView method is called with
     * the current layout, so we need to manually try to scroll all scrollable parents
     * until one of them bites.
     */
    const ieScrollIntoView = (el: HTMLElement) => findScrollableParents(el).some(scrollContainer => {
        const elOffsetTop = offsetTop(el);
        // Stop scrolling if element is at the top
        if (elOffsetTop < 1.2) return true;
        const containerScrollTop = scrollContainer.scrollTop;
        const containerOffsetTop = offsetTop(scrollContainer);
        const newScrollTop = containerScrollTop - containerOffsetTop + elOffsetTop;
        scrollContainer.scrollTo(scrollContainer.scrollLeft, newScrollTop);
    });

    HTMLElement.prototype.scrollIntoView = function () {
        ieScrollIntoView(this);
    };
};

@elambro
Copy link

elambro commented Sep 11, 2020

This also doesn't work on Chrome for Android, at least on versions 38.0.2125 through 68.0.3440

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants