nav: restore scroll position on browser back #476

This commit is contained in:
Sören Weber 2023-02-12 09:35:50 +01:00
parent 02e81b6e4b
commit c7a819323a
No known key found for this signature in database
GPG key ID: BEC6D55545451B6D
2 changed files with 124 additions and 47 deletions

View file

@ -2,7 +2,12 @@ window.relearn = window.relearn || {};
window.relearn.runInitialSearch = function(){ window.relearn.runInitialSearch = function(){
if( window.relearn.isSearchInit && window.relearn.isLunrInit ){ if( window.relearn.isSearchInit && window.relearn.isLunrInit ){
searchDetail(); var input = document.querySelector('#search-by-detail');
if( !input ){
return;
}
var value = input.value;
searchDetail( value );
} }
} }
@ -40,24 +45,55 @@ function initLunrIndex( index ){
} }
function triggerSearch(){ function triggerSearch(){
searchDetail();
var input = document.querySelector('#search-by-detail'); var input = document.querySelector('#search-by-detail');
if( !input ){ if( !input ){
return; return;
} }
var value = input.value; var value = input.value;
searchDetail( value );
// add a new entry to the history after the user
// changed the term; this does not reload the page
// but will add to the history and update the address bar URL
var url = new URL( window.location ); var url = new URL( window.location );
var oldValue = url.searchParams.get('search-by'); var oldValue = url.searchParams.get( 'search-by' );
if( value != oldValue ){ if( value != oldValue ){
url.searchParams.set('search-by', value); var state = window.history.state || {};
window.history.pushState(url.toString(), '', url); state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
url.searchParams.set( 'search-by', value );
state.search = url.toString();
// with normal pages, this is handled by the 'pagehide' event, but this
// doesn't fire in case of pushState, so we have to do the same thing
// here, too
state.contentScrollTop = elc.scrollTop;
window.history.pushState( state, '', url );
} }
} }
window.addEventListener('popstate', function ( event ) { window.addEventListener( 'popstate', function ( event ){
// restart search if browsed thru history // restart search if browsed thru history
if (event.state && event.state.indexOf('search.html?search-by=') >= 0) { if( event.state ){
window.location.reload(); var state = window.history.state || {};
state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
if( state.search ) {
var url = new URL( state.search );
if( url.searchParams.has('search-by') ){
var search = url.searchParams.get( 'search-by' );
// we have to insert the old search term into the inputs
var inputs = document.querySelectorAll( 'input.search-by' );
inputs.forEach( function( e ){
e.value = search;
var event = document.createEvent( 'Event' );
event.initEvent( 'input', false, false );
e.dispatchEvent( event );
});
// recreate the last search results and eventually
// restore the previous scrolling position
searchDetail( search );
}
}
} }
}); });
@ -161,12 +197,7 @@ function resolvePlaceholders( s, args ) {
}); });
}; };
function searchDetail() { function searchDetail( value ) {
var input = document.querySelector('#search-by-detail');
if( !input ){
return;
}
var value = input.value;
var results = document.querySelector('#searchresults'); var results = document.querySelector('#searchresults');
var hint = document.querySelector('.searchhint'); var hint = document.querySelector('.searchhint');
hint.innerText = ''; hint.innerText = '';
@ -201,14 +232,32 @@ function searchDetail() {
} }
input.focus(); input.focus();
setTimeout( adjustContentWidth, 0 ); setTimeout( adjustContentWidth, 0 );
// if we are initiating search because of a browser history
// operation, we have to restore the scrolling postion the
// user previously has used; if this search isn't initiated
// by a browser history operation, it simply does nothing
var state = window.history.state || {};
state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
if( state.contentScrollTop ){
window.setTimeout( function(){
elc.scrollTop = state.contentScrollTop;
}, 10 );
return;
}
} }
initLunrJson(); initLunrJson();
initLunrJs(); initLunrJs();
function startSearch(){ function startSearch(){
var url = new URL( window.location ); var input = document.querySelector('#search-by-detail');
window.history.replaceState(url.toString(), '', url); if( input ){
var state = window.history.state || {};
state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
state.search = window.location.toString();
window.history.replaceState( state, '', window.location );
}
var searchList = new autoComplete({ var searchList = new autoComplete({
/* selector for the search box element */ /* selector for the search box element */

View file

@ -815,31 +815,69 @@ function initHistory() {
} }
} }
function scrollToActiveMenu() { function initScrollPositionSaver(){
window.setTimeout(function(){ function savePosition( event ){
var state = window.history.state || {};
state = Object.assign( {}, ( typeof state === 'object' ) ? state : {} );
state.contentScrollTop = elc.scrollTop;
window.history.replaceState( state, '', window.location );
};
window.addEventListener( 'pagehide', savePosition );
}
function scrollToPositions() {
// show active menu entry
window.setTimeout( function(){
var e = document.querySelector( '#sidebar ul.topics li.active a' ); var e = document.querySelector( '#sidebar ul.topics li.active a' );
if( e && e.scrollIntoView ){ if( e && e.scrollIntoView ){
e.scrollIntoView({ e.scrollIntoView({
block: 'center', block: 'center',
}); });
} }
}, 10); }, 10 );
}
function scrollToFragment() { // scroll the content to point of interest;
if( !window.location.hash || window.location.hash.length <= 1 ){ // if we have a scroll position saved, the user was here
// before in his history stack and we want to reposition
// to the position he was when he left the page;
// otherwise if he used page search before, we want to position
// to its last outcome;
// otherwise he may want to see a specific fragment
var state = window.history.state || {};
state = ( typeof state === 'object') ? state : {};
if( state.contentScrollTop !== undefined ){
window.setTimeout( function(){
elc.scrollTop = state.contentScrollTop;
}, 10 );
return;
}
var searchValue = sessionStorage.getItem( baseUriFull+'search-value' );
var found = elementContains( searchValue, elc );
var searchedElem = found.length && found[ 0 ];
if( searchedElem ){
searchedElem.scrollIntoView( true );
var scrolledY = window.scrollY;
if( scrolledY ){
window.scroll( 0, scrolledY - 125 );
}
return;
}
if( window.location.hash && window.location.hash.length > 1 ){
window.setTimeout( function(){
try{
var e = document.querySelector( window.location.hash );
if( e && e.scrollIntoView ){
e.scrollIntoView({
block: 'start',
});
}
} catch( e ){}
}, 10 );
return; return;
} }
window.setTimeout(function(){
try{
var e = document.querySelector( window.location.hash );
if( e && e.scrollIntoView ){
e.scrollIntoView({
block: 'start',
});
}
} catch( e ){}
}, 10);
} }
function mark() { function mark() {
@ -1064,25 +1102,15 @@ function initSearch() {
} }
mark(); mark();
// set initial search value on page load // set initial search value for inputs on page load
if( sessionStorage.getItem( baseUriFull+'search-value' ) ){ if( sessionStorage.getItem( baseUriFull+'search-value' ) ){
var searchValue = sessionStorage.getItem( baseUriFull+'search-value' ); var search = sessionStorage.getItem( baseUriFull+'search-value' );
inputs.forEach( function( e ){ inputs.forEach( function( e ){
e.value = searchValue; e.value = search;
var event = document.createEvent( 'Event' ); var event = document.createEvent( 'Event' );
event.initEvent( 'input', false, false ); event.initEvent( 'input', false, false );
e.dispatchEvent( event ); e.dispatchEvent( event );
}); });
var found = elementContains( searchValue, document.querySelector( '#body-inner' ) );
var searchedElem = found.length && found[ 0 ];
if( searchedElem ){
searchedElem.scrollIntoView( true );
var scrolledY = window.scrollY;
if( scrolledY ){
window.scroll( 0, scrolledY - 125 );
}
}
} }
window.relearn.isSearchInit = true; window.relearn.isSearchInit = true;
@ -1094,8 +1122,6 @@ ready( function(){
initMermaid(); initMermaid();
initSwagger(); initSwagger();
initMenuScrollbar(); initMenuScrollbar();
scrollToActiveMenu();
scrollToFragment();
initToc(); initToc();
initAnchorClipboard(); initAnchorClipboard();
initCodeClipboard(); initCodeClipboard();
@ -1104,6 +1130,8 @@ ready( function(){
initHistory(); initHistory();
initSearch(); initSearch();
initImage(); initImage();
initScrollPositionSaver();
scrollToPositions();
}); });
function useMermaid( config ){ function useMermaid( config ){