Expand collapsed elements when the browser tries to scroll to a child

Known issue: If you scroll to a fragment via hash, collapse a parent,
and then click a link to the same fragment or press Enter in the address
bar, the scrolling won't happen. This is because the hashchange event
doesn't get fired, and I doubt there's a good solution.

Bug: T276741
Change-Id: Ibd42d4a051b7bb07beeea4a36b0dc5977f315518
This commit is contained in:
Nardog 2021-03-21 16:54:16 +00:00 committed by [[mw:User:Nardog]]
parent c4d687a162
commit 599c80d06e
2 changed files with 67 additions and 0 deletions

View file

@ -156,6 +156,49 @@
toggleElement( $collapsible, wasCollapsed ? 'expand' : 'collapse', $toggle, options );
}
/**
* If the URL contains a hash followed by the fragment identifier of an
* element inside collapsed parents, expand them all and scroll to it.
*
* @private
*/
function hashHandler() {
var fragmentId, fragment, $parents;
fragmentId = window.location.hash.slice( 1 );
if ( !fragmentId ) {
// The hash is empty
return;
}
fragment = document.getElementById( fragmentId );
if ( !fragment ) {
// The fragment doesn't exist
return;
}
$parents = $( fragment ).parents( '.mw-collapsed' );
if ( !$parents.length ) {
// The fragment is not in a collapsed element
return;
}
// Expand collapsed parents
$parents.each( function () {
var $collapsible = $( this );
if ( $collapsible.data( 'mw-made-collapsible' ) ) {
$collapsible.data( 'mw-collapsible' ).expand();
} else {
// The collapsible has not been initialized, so just prevent it
// from being collapsed
$collapsible.removeClass( 'mw-collapsed' );
}
} );
// Scroll to the fragment
fragment.scrollIntoView();
}
/**
* Enable collapsible-functionality on all elements in the collection.
*
@ -356,6 +399,9 @@
} );
// Attach hash handler
window.addEventListener( 'hashchange', hashHandler );
/**
* Fired after collapsible content has been initialized
*
@ -370,6 +416,9 @@
return this;
};
// Run hash handler right now in case the URL already has a hash
hashHandler();
/**
* @class jQuery
* @mixins jQuery.plugin.makeCollapsible

View file

@ -377,6 +377,24 @@
$clone.find( '.mw-collapsible-toggle a' ).trigger( 'click' );
} );
QUnit.test( 'reveal hash fragment', function ( assert ) {
var $collapsible = prepareCollapsible(
'<div class="mw-collapsible mw-collapsed">' + loremIpsum + '<div id="div,a:nth-child(even)">' + loremIpsum + '</div></div>'
),
fragment = document.getElementById( 'div,a:nth-child(even)' ),
done = assert.async();
assert.assertTrue( fragment.offsetParent === null, 'initial: fragment is hidden' );
$collapsible.on( 'afterExpand.mw-collapsible', function () {
assert.assertTrue( fragment.offsetParent !== null, 'after hash change: fragment is visible' );
done();
window.location.hash = '';
} );
window.location.hash = 'div,a:nth-child(even)';
} );
QUnit.test( 'T168689 - nested collapsible divs should keep independent state', function ( assert ) {
var $collapsible1 = prepareCollapsible(
'<div class="mw-collapsible">' + loremIpsum + '</div>'