Ver Fonte

Always jump article to top when header is offscreen, also when 'Stick the article to the top when opened' is disabled (#8870)

* Always jump article to top when header is offscreen

Closes #4069.

How to test the feature manually:

1. Disable 'Stick the article to the top when opened'
2. Open a very long article
3. No scrolling will occur if you close it again while the header is still onscreen
4. If the header left the screen, opening another article will scroll to it to top 
5. Same for collapsing the current article, but then it'll put the current header at the top

* Compensate for layout shift

* Fix going back to previous article above viewport

* clarify variable name: header_off_screen → header_above_viewport
Frans de Jonge há 3 semanas atrás
pai
commit
aaae9ee388
1 ficheiros alterados com 43 adições e 8 exclusões
  1. 43 8
      p/scripts/main.js

+ 43 - 8
p/scripts/main.js

@@ -463,6 +463,20 @@ function toggleContent(new_active, old_active, skipping) {
 		loadLazyImages(new_active);
 	}
 
+	const relative_move = context.current_view === 'global';
+	const box_to_move = relative_move ? document.getElementById('panel') : document.scrollingElement;
+
+	const old_scrollTop = box_to_move.scrollTop;
+	const old_offsetTop = new_active.offsetTop;
+
+	const nav_menu = document.querySelector('.nav_menu');
+	let nav_menu_height = 0;
+	if (nav_menu && (getComputedStyle(nav_menu).position === 'fixed' || getComputedStyle(nav_menu).position === 'sticky')) {
+		nav_menu_height = nav_menu.offsetHeight;
+	}
+
+	const flux_header = new_active.querySelector('.flux_header');
+
 	if (old_active !== new_active) {
 		if (!skipping) {
 			new_active.classList.add('active');
@@ -479,18 +493,22 @@ function toggleContent(new_active, old_active, skipping) {
 		new_active.classList.toggle('active');
 	}
 
-	const relative_move = context.current_view === 'global';
-	const box_to_move = relative_move ? document.getElementById('panel') : document.scrollingElement;
+	const new_offsetTop = new_active.offsetTop;
+	const layout_shift = new_offsetTop - old_offsetTop;
+
+	const prev_article = new_active.previousElementSibling;
 
-	if (context.sticky_post) {	// Stick the article to the top when opened
-		const prev_article = new_active.previousElementSibling;
-		const nav_menu = document.querySelector('.nav_menu');
-		let nav_menu_height = 0;
+	let header_above_viewport = false;
 
-		if (nav_menu && (getComputedStyle(nav_menu).position === 'fixed' || getComputedStyle(nav_menu).position === 'sticky')) {
-			nav_menu_height = nav_menu.offsetHeight;
+	if (!context.sticky_post) {
+		// Compensate for layout shift to maintain visual position
+		box_to_move.scrollTop = old_scrollTop + layout_shift;
+		if (flux_header) {
+			header_above_viewport = flux_header.getBoundingClientRect().top < nav_menu_height;
 		}
+	}
 
+	if (context.sticky_post || header_above_viewport) {	// Stick the article to the top when opened, or when header is off-screen
 		let new_pos = new_active.offsetParent.offsetTop + new_active.offsetTop - nav_menu_height;
 
 		if (prev_article && prev_article.offsetParent && new_active.offsetTop - prev_article.offsetTop <= 150) {
@@ -506,6 +524,23 @@ function toggleContent(new_active, old_active, skipping) {
 		}
 
 		box_to_move.scrollTop = new_pos;
+	} else {
+		// If the header is below the viewport, scroll down just enough to bring it fully into view
+		if (flux_header) {
+			let bottom = flux_header.getBoundingClientRect().bottom;
+			const inner_header = new_active.querySelector('.flux_content header');
+			if (inner_header) {
+				bottom = Math.max(bottom, inner_header.getBoundingClientRect().bottom);
+			}
+			let overflow = bottom - window.innerHeight;
+			if (overflow > 0) {
+				const max_overflow = flux_header.getBoundingClientRect().top - nav_menu_height;
+				if (overflow > max_overflow) {
+					overflow = max_overflow > 0 ? max_overflow : 0;
+				}
+				box_to_move.scrollTop += overflow;
+			}
+		}
 	}
 
 	if (new_active.classList.contains('active') && !skipping) {