dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Improve interaction between enhanced nav and scroll position #51646

Open SteveSandersonMS opened 10 months ago

SteveSandersonMS commented 10 months ago

Enhanced nav produces different scroll position behaviors than normal navigation. Ideally it would be approximately the same.

SteveSandersonMS commented 10 months ago

See also https://github.com/dotnet/aspnetcore/issues/51338 which we closed but is valid

javiercn commented 10 months ago

@SteveSandersonMS do you think we should patch this behavior? or is it fine being something we potentially tackle in 9.0

SteveSandersonMS commented 10 months ago

I was expecting it would be too big of a feature to put into a patch. What's your view?

ynnob commented 10 months ago

@javiercn @SteveSandersonMS I really dislike the current way this works. So looking at #51338 we should use some js to stop this? But on the other hand i don't think the current behavior of enhanced nav is even correct.

When scrolling down a page and navigating to a link in the footer it is always acting super weird and your scroll position will not even be at the position you were before.

Checkout my page atm. It's running .NET 8 RC2. If you visit my Blazor Page https://ynnob.com/ and go to Privacy Policy and scroll to the footer and navigate to Datenschutz you are not at the bottom anymore. So even that is not working correctly.

EDIT: nvm 🤡 it is working correctly. The Datenschutz page is longer then the privacy policy so it just jumps up. But this is still confusing. So what is the best way to handle this. Always scroll to the top with js?

Bartmax commented 10 months ago

I believe the browser restores scroll when page fully loads, so can't we fake SSR until everything is rendered and let the browser restores the scroll by itself. Actually this will be very aligned to normal (streaming) server render behavior

ghost commented 10 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

fyiweb commented 10 months ago

I think this needs to be patched in .NET 8. A navigation event should always scroll to the top by default, including same page.

In the linked closed issue, 51338, the comment was made that it would make form submissions weird. The same argument can be made of the current behavior. Regular form submissions almost always scroll to the top when there's an error, where the dotnet Blazor templates even place the validation summary message, or custom scrolling is implemented to jump to a form validation error. Leaving the viewport at the form submit button leaves the user confused on if it went through or not and is a terrible user experience.

The comment was also made that this behavior, not resetting to the top on enhanced navigation, is in line with SPA apps. Which ones exactly? Because React navigation events scroll to the top, and retaining the current position requires implementing additional code: https://reactrouter.com/en/main/components/scroll-restoration If this was in reference to just form submissions, which, granted, a React form submission would not cause the scrolling to reset, then at least fix the behavior elsewhere, such as on NavLinks.

Retaining the scroll position should be an optional parameter on NavLinks and NavigationManager.NavigateTo() that defaults to false, and arguably the same on EditForm.

I'm seeing normal scroll position recovery for enhanced navigation on browser back and forward, so no problems there.

Please consider patching this in .NET 8 LTS.

Also, I don't want to sound ungrateful - I appreciate the hard work on .NET 8 Blazor! It's an amazing framework and I'm loving the new Identity pages! ❤️

ladeak commented 9 months ago

I am trying to migrate a prerendered webassembly application to SSR and a few components InteractiveWebAssembly rendered. I am using non-streaming rendering, and I also notice the scroll position is not reset when clicking on anchor tags. This is really unexpected, I would be happy with a patch or a workaround.

I also notice that some of the JS libraries within the webassembly components don't get rendered correctly on navigation.

ladeak commented 9 months ago

Is there a workaround? I tried adding an interactive wa rendered component with some JS interop to scroll to the top, but it only works on refresh.

EDIT: I assume one workaround could be:

    <script>
        const observeUrlChange = () => {
            let oldHref = document.location.href;
            const body = document.querySelector("body");
            const observer = new MutationObserver(mutations => {
                if (oldHref !== document.location.href) {
                    oldHref = document.location.href;
                    window.scroll(0, 0);
                }
            });
            observer.observe(body, { childList: true, subtree: true });
        };
        window.onload = observeUrlChange;
    </script>
vpekarek commented 9 months ago

The previous script did not work for me, not sure why.

So here is different way how to do it using the Blazor enhancedload event:

<script>
    window.interceptNavigation = () => {
        let currentUrl = window.location.href;
        Blazor.addEventListener('enhancedload', () => {
            let newUrl = window.location.href;
            if (currentUrl != newUrl) {
                window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
            }
            currentUrl = newUrl;
        });
    };

    document.onload += window.interceptNavigation();
</script>

Place this script just after <script src="_framework/blazor.web.js"></script>.

ghost commented 8 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

heron1 commented 7 months ago

Clicking a link and scrolling to the top of a new page is a base requirement of a web app. How is this not part of .NET 8 for all its fame and glory? It seems the most simplest of features are always passed to yet another major .NET release, and people wonder why no serious real world app uses blazor yet for B2C.

Scrolling to the top of a new page is very, very much required functionality for how the Internet works, and has worked since the 90s. I must question the overall management decisions of the people in charge of Blazor for thinking maintaining scroll positions on different page navigations without an option to turn it off was a good idea. I mean, seriously?

vpekarek commented 7 months ago

The thing here is, that for example the Vue.js have the same behavior, but it is easy to override it in the routing middleware. In the Blazor, I did not find any 100% working solution.

Fydar commented 6 months ago

I would greatly appreciate having backwards and forward navigation working correctly. It is so incredibly infuriating to have to scroll down my work-in-progress portfolio every time I click on a project and head back to the home page.

Until then, I will need to disable the enhanced navigation script.

To restore the original browser behaviour (and fix the backward and forward navigation not having the correct scroll positions), you can disable "enhanced navigation" in the App.razor by replacing:

<script src="_framework/blazor.web.js"></script>

With:

<script src="_framework/blazor.web.js" autostart="false"></script>
<script>
    Blazor.start({
        ssr: { disableDomPreservation: true }
    });
</script>
Bartmax commented 5 months ago

It is so incredibly infuriating

I disabled it too, very very very frustrating.

Kebechet commented 2 months ago

I am not able to find any workaround for Blazor hybrid. Try to link this functionality with: history.scrollRestoration property. https://developer.mozilla.org/en-US/docs/Web/API/History/scrollRestoration

Quemuel-Nassor commented 1 month ago

@vpekarek's suggestion worked perfectly for me, I made a slightly improved version that provides an event for custom actions when there is a change of parameters on the page


/**
 * get url data
 * @returns { urlBase, filterKeys, filterValues }
 */
function getUrlInfo() {
    let url = new URL(window.location.href);
    let urlBase = `${url.origin}${url.pathname}`;
    let filterKeys = Array.from(url.searchParams.keys());
    let filterValues = Array.from(url.searchParams.values());

    return { urlBase, filterKeys, filterValues };
}

/**
 * handle scroll behavior on enhanced navigation 
 */
window.enhancedNavigationHandler = () => {

    let currentUrl = getUrlInfo();

    Blazor.addEventListener('enhancedload', () => {

        let targetUrl = getUrlInfo();

        /* check if page params has changed */
        let filterKeysHasChanged = currentUrl.filterKeys.filter(e => !targetUrl.filterKeys.includes(e)).length != 0;
        let filterValuesHasChanged = currentUrl.filterValues.filter(e => !targetUrl.filterValues.includes(e)).length != 0;

        /* scroll to top if page url has changed */
        if (currentUrl.urlBase != targetUrl.urlBase)
        {
            window.scrollTo({ top: 0, left: 0, behavior: 'instant' });
        }
        /* trigger pageSearch event if only page url parameters has changed */
        else if (filterKeysHasChanged || filterValuesHasChanged)
        {
            document.dispatchEvent(new Event("pageSearch"));
        }

        currentUrl = targetUrl;
    });
};

/* pageSearch event listener sample */
//document.addEventListener("pageSearch", () => { console.log("search occourred on page") });

document.onload += window.enhancedNavigationHandler();
alex-fitplans commented 1 month ago

I too had to disable enhanced navigation in order to get normal hyperlink navigation behavior. Hopefully this can get fixed in .NET 9

Bartmax commented 1 month ago

@Quemuel-Nassor Unfortunately using this kind of hacks doesn't properly handle forward/back buttons and navigation to another page with hash #.

My flawless fix was to remove enhance nav completely and add hotwire turbo for an awesome experience.