diff --git a/src/Components/Web.JS/src/Services/NavigationEnhancement.ts b/src/Components/Web.JS/src/Services/NavigationEnhancement.ts
index 9dcd7540d866..c39a00bd0337 100644
--- a/src/Components/Web.JS/src/Services/NavigationEnhancement.ts
+++ b/src/Components/Web.JS/src/Services/NavigationEnhancement.ts
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import { synchronizeDomContent } from '../Rendering/DomMerging/DomSync';
-import { attachProgrammaticEnhancedNavigationHandler, handleClickForNavigationInterception, hasInteractiveRouter, isForSamePath, isSamePageWithHash, notifyEnhancedNavigationListeners, performScrollToElementOnTheSamePage } from './NavigationUtils';
+import { attachProgrammaticEnhancedNavigationHandler, handleClickForNavigationInterception, hasInteractiveRouter, isForSamePath, notifyEnhancedNavigationListeners, performScrollToElementOnTheSamePage, isSamePageWithHash } from './NavigationUtils';
import { resetScrollAfterNextBatch, resetScrollIfNeeded } from '../Rendering/Renderer';
/*
@@ -99,7 +99,7 @@ function onDocumentClick(event: MouseEvent) {
handleClickForNavigationInterception(event, absoluteInternalHref => {
const originalLocation = location.href;
- const shouldScrollToHash = isSamePageWithHash(absoluteInternalHref);
+ const shouldScrollToHash = isSamePageWithHash(originalLocation, absoluteInternalHref);
history.pushState(null, /* ignored title */ '', absoluteInternalHref);
if (shouldScrollToHash) {
@@ -120,6 +120,11 @@ function onPopState(state: PopStateEvent) {
return;
}
+ if (state.state == null && isSamePageWithHash(currentContentUrl, location.href)) {
+ currentContentUrl = location.href;
+ return;
+ }
+
// load the new page
performEnhancedPageLoad(location.href, /* interceptedLink */ false);
}
diff --git a/src/Components/Web.JS/src/Services/NavigationManager.ts b/src/Components/Web.JS/src/Services/NavigationManager.ts
index b3352b399f55..8e2de809505a 100644
--- a/src/Components/Web.JS/src/Services/NavigationManager.ts
+++ b/src/Components/Web.JS/src/Services/NavigationManager.ts
@@ -150,7 +150,7 @@ function performExternalNavigation(uri: string, replace: boolean) {
async function performInternalNavigation(absoluteInternalHref: string, interceptedLink: boolean, replace: boolean, state: string | undefined = undefined, skipLocationChangingCallback = false) {
ignorePendingNavigation();
- if (isSamePageWithHash(absoluteInternalHref)) {
+ if (isSamePageWithHash(location.href, absoluteInternalHref)) {
saveToBrowserHistory(absoluteInternalHref, replace, state);
performScrollToElementOnTheSamePage(absoluteInternalHref);
return;
diff --git a/src/Components/Web.JS/src/Services/NavigationUtils.ts b/src/Components/Web.JS/src/Services/NavigationUtils.ts
index bc58636c39c6..9976eafc898c 100644
--- a/src/Components/Web.JS/src/Services/NavigationUtils.ts
+++ b/src/Components/Web.JS/src/Services/NavigationUtils.ts
@@ -47,9 +47,11 @@ export function isWithinBaseUriSpace(href: string) {
&& (nextChar === '' || nextChar === '/' || nextChar === '?' || nextChar === '#');
}
-export function isSamePageWithHash(absoluteHref: string): boolean {
- const url = new URL(absoluteHref);
- return url.hash !== '' && location.origin === url.origin && location.pathname === url.pathname && location.search === url.search;
+export function isSamePageWithHash(oldUrl: string, newUrl: string): boolean {
+ const a = new URL(oldUrl);
+ const b = new URL(newUrl);
+ return a.origin === b.origin && a.pathname === b.pathname
+ && a.search === b.search && b.hash !== '';
}
export function isForSamePath(url1: string, url2: string) {
diff --git a/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs
index 49ca00e06915..c11a30786c2e 100644
--- a/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs
+++ b/src/Components/test/E2ETest/ServerRenderingTests/EnhancedNavigationTest.cs
@@ -10,6 +10,7 @@
using Microsoft.AspNetCore.InternalTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.BiDi.Communication;
+using OpenQA.Selenium.DevTools;
using OpenQA.Selenium.Support.Extensions;
using TestServer;
using Xunit.Abstractions;
@@ -195,6 +196,40 @@ public void CanScrollToHashWithoutPerformingFullNavigation()
.EndsWith("scroll-to-hash", StringComparison.Ordinal));
}
+ [Fact]
+ public void NonEnhancedNavCanScrollToHashWithoutFetchingPageAnchor()
+ {
+ Navigate($"{ServerPathBase}/nav/scroll-to-hash");
+ var originalTextElem = Browser.Exists(By.CssSelector("#anchor #text"));
+ Browser.Equal("Text", () => originalTextElem.Text);
+
+ Browser.Exists(By.CssSelector("#anchor #scroll-anchor")).Click();
+ Browser.True(() => Browser.GetScrollY() > 500);
+ Browser.True(() => Browser
+ .Exists(By.CssSelector("#anchor #uri-on-page-load"))
+ .GetDomAttribute("data-value")
+ .EndsWith("scroll-to-hash", StringComparison.Ordinal));
+
+ Browser.Equal("Text", () => originalTextElem.Text);
+ }
+
+ [Fact]
+ public void NonEnhancedNavCanScrollToHashWithoutFetchingPageNavLink()
+ {
+ Navigate($"{ServerPathBase}/nav/scroll-to-hash");
+ var originalTextElem = Browser.Exists(By.CssSelector("#navlink #text"));
+ Browser.Equal("Text", () => originalTextElem.Text);
+
+ Browser.Exists(By.CssSelector("#navlink #scroll-anchor")).Click();
+ Browser.True(() => Browser.GetScrollY() > 500);
+ Browser.True(() => Browser
+ .Exists(By.CssSelector("#navlink #uri-on-page-load"))
+ .GetDomAttribute("data-value")
+ .EndsWith("scroll-to-hash", StringComparison.Ordinal));
+
+ Browser.Equal("Text", () => originalTextElem.Text);
+ }
+
[Theory]
[InlineData("server")]
[InlineData("webassembly")]
diff --git a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/EnhancedNav/PageForScrollingToHash.razor b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/EnhancedNav/PageForScrollingToHash.razor
index 102e18a84807..22597ce37be3 100644
--- a/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/EnhancedNav/PageForScrollingToHash.razor
+++ b/src/Components/test/testassets/Components.TestServer/RazorComponents/Pages/EnhancedNav/PageForScrollingToHash.razor
@@ -1,6 +1,7 @@
@page "/nav/scroll-to-hash"
@attribute [StreamRendering]
@inject NavigationManager NavigationManager
+@using Microsoft.AspNetCore.Components.Forms
Text
+