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(){
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(){
searchDetail();
var input = document.querySelector('#search-by-detail');
if( !input ){
return;
}
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 oldValue = url.searchParams.get('search-by');
var oldValue = url.searchParams.get( 'search-by' );
if( value != oldValue ){
url.searchParams.set('search-by', value);
window.history.pushState(url.toString(), '', url);
var state = window.history.state || {};
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
if (event.state && event.state.indexOf('search.html?search-by=') >= 0) {
window.location.reload();
if( event.state ){
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() {
var input = document.querySelector('#search-by-detail');
if( !input ){
return;
}
var value = input.value;
function searchDetail( value ) {
var results = document.querySelector('#searchresults');
var hint = document.querySelector('.searchhint');
hint.innerText = '';
@ -201,14 +232,32 @@ function searchDetail() {
}
input.focus();
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();
initLunrJs();
function startSearch(){
var url = new URL( window.location );
window.history.replaceState(url.toString(), '', url);
var input = document.querySelector('#search-by-detail');
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({
/* selector for the search box element */

View file

@ -815,22 +815,58 @@ function initHistory() {
}
}
function scrollToActiveMenu() {
window.setTimeout(function(){
function initScrollPositionSaver(){
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' );
if( e && e.scrollIntoView ){
e.scrollIntoView({
block: 'center',
});
}
}, 10);
}
}, 10 );
function scrollToFragment() {
if( !window.location.hash || window.location.hash.length <= 1 ){
// scroll the content to point of interest;
// 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;
}
window.setTimeout(function(){
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 ){
@ -839,7 +875,9 @@ function scrollToFragment() {
});
}
} catch( e ){}
}, 10);
}, 10 );
return;
}
}
function mark() {
@ -1064,25 +1102,15 @@ function initSearch() {
}
mark();
// set initial search value on page load
// set initial search value for inputs on page load
if( sessionStorage.getItem( baseUriFull+'search-value' ) ){
var searchValue = sessionStorage.getItem( baseUriFull+'search-value' );
var search = sessionStorage.getItem( baseUriFull+'search-value' );
inputs.forEach( function( e ){
e.value = searchValue;
e.value = search;
var event = document.createEvent( 'Event' );
event.initEvent( 'input', false, false );
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;
@ -1094,8 +1122,6 @@ ready( function(){
initMermaid();
initSwagger();
initMenuScrollbar();
scrollToActiveMenu();
scrollToFragment();
initToc();
initAnchorClipboard();
initCodeClipboard();
@ -1104,6 +1130,8 @@ ready( function(){
initHistory();
initSearch();
initImage();
initScrollPositionSaver();
scrollToPositions();
});
function useMermaid( config ){