fix(stickyHeader): 🐛 add a placeholder element to page header to avoid layout shift when stickied

Closes: #854
This commit is contained in:
alistair3149 2024-07-07 17:58:02 -04:00
parent e9fd488cee
commit e5336c4610
No known key found for this signature in database
3 changed files with 37 additions and 7 deletions

View file

@ -39,17 +39,41 @@ function init() {
return; return;
} }
const toggleStickyClass = ( state ) => () => { const placeholder = document.createElement( 'div' );
placeholder.id = 'citizen-page-header-sticky-placeholder';
header.insertAdjacentElement( 'afterend', placeholder );
let staticHeaderHeight = header.offsetHeight;
const toggleStickyHeader = ( isSticky ) => {
window.requestAnimationFrame( () => { window.requestAnimationFrame( () => {
document.body.classList.toggle( STICKY_CLASS, state ); document.body.classList.toggle( STICKY_CLASS, isSticky );
placeholder.style.height = `${ staticHeaderHeight - header.offsetHeight }px`;
} ); } );
}; };
const observer = initIntersectionObserver( const observer = initIntersectionObserver(
toggleStickyClass( true ), () => {
toggleStickyClass( false ) toggleStickyHeader( true );
},
() => {
toggleStickyHeader( false );
}
); );
observer.observe( sentinel ); observer.observe( sentinel );
const onResize = () => {
toggleStickyHeader( false );
};
const onResizeEnd = () => {
// Refresh static header height after resize
staticHeaderHeight = header.offsetHeight;
toggleStickyHeader( true );
};
window.addEventListener( 'resize', onResize );
window.addEventListener( 'resize', mw.util.debounce( onResizeEnd, 250 ) );
} }
module.exports = { module.exports = {

View file

@ -1,10 +1,14 @@
#citizen-page-header-sticky-sentinel { #citizen-page-header-sticky-sentinel {
grid-area: content; // align right above content grid-area: afterHeader; // align right above content
height: 1px; height: 1px;
visibility: hidden; visibility: hidden;
contain: strict; contain: strict;
} }
#citizen-page-header-sticky-placeholder {
grid-area: afterHeader;
}
.ve-activated, .ve-activated,
.action-edit { .action-edit {
// HACK: So sticky header will never trigger in edit action // HACK: So sticky header will never trigger in edit action

View file

@ -13,7 +13,8 @@
.citizen-body-container { .citizen-body-container {
display: grid; display: grid;
grid-template-areas: grid-template-areas:
'header' 'header'
'afterHeader'
'content' 'content'
'footer'; 'footer';
// Using auto as min value will cause overflow // Using auto as min value will cause overflow
@ -59,7 +60,8 @@
.citizen-toc-enabled { .citizen-toc-enabled {
.citizen-body-container { .citizen-body-container {
grid-template-areas: grid-template-areas:
'header header' 'header header'
'afterHeader afterHeader'
'content sidebar' 'content sidebar'
'footer footer'; 'footer footer';
grid-template-columns: minmax( 0, var( --width-layout ) ) var( --width-toc ); grid-template-columns: minmax( 0, var( --width-layout ) ) var( --width-toc );