Widget:ScrollToTopSidebar: Difference between revisions
(Scroll-to-top button: darker band fills left to right with reading progress (hard edge)) |
(Scroll-to-top fill starts when the button comes into view, not at the top of the content) |
||
| Line 30: | Line 30: | ||
}); | }); | ||
// Progress | // Progress runs from the moment the button comes into view (it sits at the foot of a | ||
// tall sticky sidebar, so that can be well down the page) to the end of the main | |||
// content column beside the sidebar (the .col-md-9 sibling, or the whole page). | |||
const sttRow = container.closest('.row'); | const sttRow = container.closest('.row'); | ||
const sttContent = (sttRow && sttRow.querySelector(':scope > .col-md-9')) || document.documentElement; | const sttContent = (sttRow && sttRow.querySelector(':scope > .col-md-9')) || document.documentElement; | ||
let sttStart = 300; | |||
function sttProgress() { | function sttProgress() { | ||
const | const y = window.pageYOffset, vh = window.innerHeight; | ||
const | // While the button is still below the fold, estimate the scroll position at which it | ||
if ( | // will be fully in view; once visible (the sidebar sticks), keep that estimate. | ||
return Math.min(1, Math.max(0, - | const btnBottom = container.getBoundingClientRect().top + (scrollToTopButtonSb.offsetHeight || 50); | ||
if (btnBottom > vh) sttStart = Math.max(300, y + btnBottom - vh); | |||
const end = y + sttContent.getBoundingClientRect().bottom - vh; | |||
if (end <= sttStart) return 1; | |||
return Math.min(1, Math.max(0, (y - sttStart) / (end - sttStart))); | |||
} | } | ||
Revision as of 18:23, 30 September 2026
<style> /* A darker band fills the button from the left as you read down the content column (--stt-p, set below). */
- scrollToTop {
background: linear-gradient(to right, rgba(186, 27, 29, .3) var(--stt-p, 0%), rgba(186, 27, 29, .1) var(--stt-p, 0%)); }
- scrollToTop:hover {
background: linear-gradient(to right, rgba(186, 27, 29, .5) var(--stt-p, 0%), rgba(186, 27, 29, .3) var(--stt-p, 0%)); } </style> <script> // Create the scroll to top button const scrollToTopButtonSb = document.createElement('button'); scrollToTopButtonSb.innerHTML = 'to top '; scrollToTopButtonSb.setAttribute('id', 'scrollToTop');
// Add the button to the document body const container = document.getElementById('scrollToTopContainer'); container.appendChild(scrollToTopButtonSb);
// Handle initial hash navigation window.addEventListener('load', () => { if (window.location.hash) { setTimeout(() => { const element = document.querySelector(window.location.hash); if (element) { element.scrollIntoView({ behavior: 'smooth' }); } }, 100); } });
// Progress runs from the moment the button comes into view (it sits at the foot of a // tall sticky sidebar, so that can be well down the page) to the end of the main // content column beside the sidebar (the .col-md-9 sibling, or the whole page). const sttRow = container.closest('.row'); const sttContent = (sttRow && sttRow.querySelector(':scope > .col-md-9')) || document.documentElement; let sttStart = 300;
function sttProgress() { const y = window.pageYOffset, vh = window.innerHeight; // While the button is still below the fold, estimate the scroll position at which it // will be fully in view; once visible (the sidebar sticks), keep that estimate. const btnBottom = container.getBoundingClientRect().top + (scrollToTopButtonSb.offsetHeight || 50); if (btnBottom > vh) sttStart = Math.max(300, y + btnBottom - vh); const end = y + sttContent.getBoundingClientRect().bottom - vh; if (end <= sttStart) return 1; return Math.min(1, Math.max(0, (y - sttStart) / (end - sttStart))); }
// Show/hide the button and advance its fill with scroll position let sttTicking = false; function sttUpdate() { sttTicking = false; if (window.pageYOffset > 300) { scrollToTopButtonSb.style.display = 'block'; scrollToTopButtonSb.style.setProperty('--stt-p', (sttProgress() * 100).toFixed(2) + '%'); } else { scrollToTopButtonSb.style.display = 'none'; } } window.addEventListener('scroll', () => { if (!sttTicking) { sttTicking = true; window.requestAnimationFrame(sttUpdate); } }, { passive: true });
// Scroll to top when the button is clicked scrollToTopButtonSb.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); </script>