2

Working on scroll position when user goes back to the previous page then same scroll position of the product will show. I have tried like this

$('.test').on('click', function(e){

        stateData = {
            path: window.location.href,
            scrollTop: $(window).scrollTop()
        };
        const url = new URL(window.location);
        window.history.replaceState(stateData, '', url);
        stateData = {
            path: window.location.href,
            scrollTop: 0
        };
        window.history.pushState(stateData, '', url);
        e.preventDefault();;
    });

But here I'm not getting the exact position due to lazy load images to scroll moving at the bottom of the page. Can anyone suggest to me how to handle it?

Mohammed Wahed Khan
  • 836
  • 2
  • 14
  • 35
  • I think you need to provide more details about your problem, - otherwhise i would assume that you want to enter a new page at a certain scroll position like described in [this SO Answer](https://stackoverflow.com/a/70154003/7195155). – toffler May 04 '22 at 12:13
  • How about saving the scrolling position in a local storage and force it when the lazy loader being loaded? We can keep developing this solution, but I'm gonna need you to add rips of code from the lazy loader that interrupt. – Amit Rahav May 11 '22 at 10:27

3 Answers3

3

In my view, the reason you are not able to reach the exact position is due to image rendering later thus changing the height of the screen. What if you add a skeleton for all the images to be loaded, that can help you to make the screen height similar to that of the one you have when everything is loaded.

poo
  • 1,080
  • 3
  • 10
0

Perhaps save the scroll position in localStorage and then go to it when the page is reloaded?

as seen here

Check if the key exists (and if it does - scroll to it) right after DOMContentLoaded, i think, otherwise it will wait for all the images to load.

You can also choose to save the scroll position after every scroll, and not only on unload

Yarin_007
  • 1,449
  • 1
  • 10
  • 17
0

Maybe someone can figure out how to use https://developer.mozilla.org/en-US/docs/Web/API/History/scrollRestoration .. but my basic idea is to remember the scrollTop for each page. This means we have to include a value for each URL, upon returning to a page we then need load the lazy items that establish the proper scrollTop.

I have used this MDN example and this debounce script as base and will only provide the necessary code changes - I included a number of images on "first_page" and "second_page". I used to provide link to a working example, but this was taken offline.

js/lazyload.js

'use strict';
// supports older browsers - no arrow functions, spread syntax or such *sigh*
var pLAZY = ( function(){
    var version = "1.1.1"
        , loadOffset = 512
        , animatable = [ "IMG", "IFRAME" ] // tags that may/should be animated, e.g. *not* SOURCE
        , loadingClasses = [] // [ 'animated', 'rubberBand' ] // change/disable animation style here
        , lazyItems = {}
        , lazyHandler_scroll = null
        , lazyHandler_resize = null
        , forceLoad_timer = null
    ;

    function _debug(){ if( arguments.length ){ debug = parseInt( arguments[0] ); } return debug; }
    function _version(){ return version; }
    function docScroll(){ return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; } // cross-browser
    function winHeight(){ return window.innerHeight || document.documentElement.clientHeight; }

    function findLazyItems(){
        lazyItems = {};
        var candidates = document.querySelectorAll( '.lazyload,img[data-src],iframe[data-src],source[data-srcset]' );
        for( var idx = 0; idx < candidates.length; idx++ ){
            var domEl = candidates[idx]
                , curTop = domEl.offsetTop // if you fail to ensure lazy loaded items do not force a reflow lower items may be loaded sooner
            ;
            if( lazyItems.hasOwnProperty( curTop ) ){
                lazyItems[curTop].push( domEl );
            }else{
                lazyItems[curTop] = [ domEl ];
            }
        }
    }

    function loadLazy(item){
        var dsrc = item.getAttribute( 'data-src' )
            , dset = item.getAttribute( 'data-srcset' )
            , changed = false
            , worked = false
        ;
        if( dsrc && dsrc.length ){
            item.setAttribute( 'src', dsrc );
            item.removeAttribute( 'data-src' );
            changed = true;
        }
        if( dset && dset.length ){
            item.setAttribute( 'srcset', dset );
            item.removeAttribute( 'data-srcset' );
            changed = true;
        }
        if( changed ){
            worked = true;
            if( animatable.includes( item.nodeName ) ){
                for( var lcx = 0; lcx < loadingClasses.length; lcx++ ){
                    item.classList.add( loadingClasses[lcx] );
                }
            }
        }
        return worked;
    }

    function checkShowItems(){
        var curVisBar = docScroll() + winHeight() + loadOffset // winHeight, not docHeight!!
            , worked = false
            , str_pagePos = "0"
        ;
        for( str_pagePos in lazyItems ){ // pagePos is a DOM.position().top value
            var pagePos = parseInt( str_pagePos );
            var lazy = lazyItems[ pagePos ]; // the items are constantly refreshed, double-effort is considered negligible
            if( pagePos <= curVisBar ){
                for( var idx = 0; idx < lazy.length; idx++ ){
                    if( loadLazy( lazy[idx] ) ) worked = true;
                }
            }
        }
        if( worked ){
            findLazyItems();
            if( Object.keys( lazyItems ).length == 0 ){
                window.removeEventListener( 'scroll', lazyHandler_scroll );
                window.removeEventListener( 'resize', lazyHandler_resize ); // this is not required anymore either
            }
        }
    }

    function waitForLoad(index,callback){
        let count = 0
            , done = 0
            , str_pagePos = "0"
            , worked = false
        ;
        for( str_pagePos in lazyItems ){
            var lazy = lazyItems[ parseInt( str_pagePos ) ];
            for( let lx = 0; lx < lazy.length; lx++ ){
                if( count <= index ){
                    if( lazy[lx].complete ) done++;
                    count++;
                }
            }
        }
        if( done == index ){
            window.clearInterval( forceLoad_timer );
            forceLoad_timer = null;
            checkShowItems();
            if(callback) callback();
        }//else{ console.log( "%d/%d images done.", done, index ); }
    }

    function forceLoad(index,callback){
        let count = 0
            , str_pagePos = "0"
            , worked = false
        ;
        for( str_pagePos in lazyItems ){
            var lazy = lazyItems[ parseInt( str_pagePos ) ];
            for( let lx = 0; lx < lazy.length; lx++ ){
                if( count <= index ){
                    if( loadLazy( lazy[lx] ) ) worked = true;
                    count++;
                }
            }
        }
        forceLoad_timer = window.setInterval( function(){ waitForLoad(index,callback); }, 50 );

    }

    function lowestSeenImage(){
        var curVisBar = docScroll() + winHeight() + loadOffset // winHeight, not docHeight!!
            , str_pagePos = "0"
            , count = 0
        ;
        for( str_pagePos in lazyItems ){
            var pagePos = parseInt( str_pagePos );
            var lazy = lazyItems[ pagePos ];
            if( pagePos <= curVisBar ){
                count += lazy.length;
            }
        }
        return count;
    }

    function duringResizing(){
        findLazyItems();
        checkShowItems();
    }

    function hookListener( evtName, callback ){
        var throttleTimer = arguments.length > 2 ? parseInt( arguments[2] ) : 250;
        if( "Cowboy" in window ){
            window.addEventListener( evtName, Cowboy.throttle( throttleTimer, callback ) );
        }else{
            //console.log( "without the Ben 'Cowboy' Almann throttle plugin we may choke the responsiveness of the user interface" );
            window.addEventListener( evtName, callback );
        }
    }

    function initialise(){
        findLazyItems();
        var seeTBjquery = ( ( "jQuery" in window ) && $.isFunction( $.throttle ) )
            , seeTBplain = ( ( "Cowboy" in window ) && ( "throttle" in window.Cowboy ) )
        ;
        if( seeTBjquery ){
            lazyHandler_scroll = $.throttle( 250, checkShowItems );
            lazyHandler_resize = $.throttle( 250, duringResizing );
        }else{
            if( seeTBplain ){
                lazyHandler_scroll = Cowboy.throttle( 250, checkShowItems );
                lazyHandler_resize = Cowboy.throttle( 250, duringResizing );
            }else{
                lazyHandler_scroll = checkShowItems;
                lazyHandler_resize = duringResizing;
            }
        }
        window.addEventListener( 'scroll', lazyHandler_scroll );
        window.addEventListener( 'resize', lazyHandler_resize );
        checkShowItems();
    }

    if( ! window.hasOwnProperty( "_pLAZY_disable_autoInit" ) ){
        document.addEventListener( "DOMContentLoaded", function(){
            initialise();
        } );
    }

    return {
        "debug": _debug
        , "version": _version
        , "findLazyItems": findLazyItems
        , "checkShowItems": checkShowItems
        , "forceLoad": forceLoad
        , "lowestSeenImage": lowestSeenImage
        , "duringResizing": duringResizing
        , "hookListener": hookListener
        , "initialize": initialise // american spelling
        , "initialise": initialise
    };

} )();

js/ajax_nav.js

"use strict";

const ajaxRequest = new (function () {

    function closeReq () {
        oLoadingBox.parentNode && document.body.removeChild(oLoadingBox);
        bIsLoading = false;
    }

    function abortReq () {
        if (!bIsLoading) { return; }
        oReq.abort();
        closeReq();
    }

    function ajaxError () {
        alert("Unknown error.");
    }

    // NOT PART OF MDN EXAMPLE::
    function restoreScroll(){
        if( oPageInfo.url in oPageInfo.scrollTop ){
            window.scrollTo( 0, oPageInfo.scrollTop[oPageInfo.url] ); // always left-most
        }
    }
    function loadAndScroll(){
        if( "pLAZY" in window ) pLAZY.forceLoad( oPageInfo.lowestSeenImage[oPageInfo.url], restoreScroll ); 
    }
    // ::NOT PART OF MDN EXAMPLE

    function ajaxLoad () {
        var vMsg, nStatus = this.status;
        switch (nStatus) {
            case 200:
                vMsg = JSON.parse(this.responseText);
                document.title = oPageInfo.title = vMsg.page;
                document.getElementById(sTargetId).innerHTML = vMsg.content;
                if (bUpdateURL) {
                    history.pushState(oPageInfo, oPageInfo.title, oPageInfo.url);
                    bUpdateURL = false;
                    // NOT PART OF MDN EXAMPLE::
                    if( "pLAZY" in window ){
                        window.pLAZY.initialise(); 
                        if( oPageInfo.lowestSeenImage[oPageInfo.url] > -1 ){ 
                            loadAndScroll(); 
                        }else{
                            restoreScroll();
                        }
                    }//else console.log("without pLAZY no scrollPos restore possible");
                    ajaxRequest.rebuildLinks();
                    // ::NOT PART OF MDN EXAMPLE
                }
                break;
            default:
                vMsg = nStatus + ": " + (oHTTPStatus[nStatus] || "Unknown");
                switch (Math.floor(nStatus / 100)) {
                    /*
                     case 1:
                     // Informational 1xx
                     console.log("Information code " + vMsg);
                     break;
                     case 2:
                     // Successful 2xx
                     console.log("Successful code " + vMsg);
                     break;
                     case 3:
                     // Redirection 3xx
                     console.log("Redirection code " + vMsg);
                     break;
                     */
                    case 4:
                        /* Client Error 4xx */
                        alert("Client Error #" + vMsg);
                        break;
                    case 5:
                        /* Server Error 5xx */
                        alert("Server Error #" + vMsg);
                        break;
                    default:
                        /* Unknown status */
                        ajaxError();
                }
        }
        closeReq();
    }

    function filterURL (sURL, sViewMode) {
        return sURL.replace(rSearch, "") + ("?" + sURL.replace(rHost, "&").replace(rView, sViewMode ? "&" + sViewKey + "=" + sViewMode : "").slice(1)).replace(rEndQstMark, "");
    }

    function getPage (sPage) {
        if (bIsLoading) { return; }
        oReq = new XMLHttpRequest();
        bIsLoading = true;
        oReq.onload = ajaxLoad;
        oReq.onerror = ajaxError;
        // NOT IN MDN EXAMPLE ::
        oPageInfo.scrollTop[oPageInfo.url] = document.documentElement.scrollTop;
        oPageInfo.lowestSeenImage[oPageInfo.url] = ( "pLAZY" in window ) ? pLAZY.lowestSeenImage() : 0;
        // :: NOT IN MDN EXAMPLE
        if (sPage) { oPageInfo.url = filterURL(sPage, null); }
        oReq.open("get", filterURL(oPageInfo.url, "json"), true);
        oReq.send();
        oLoadingBox.parentNode || document.body.appendChild(oLoadingBox);
    }

    function requestPage (sURL) {
        if (history.pushState) {
            bUpdateURL = true;
            getPage(sURL);
        } else {
            /* Ajax navigation is not supported */
            location.assign(sURL);
        }
    }

    function processLink () {
        if (this.className === sAjaxClass) {
            requestPage(this.href);
            return false;
        }
        return true;
    }

    function init () {
        oPageInfo.title = document.title;
        for (var oLink, nIdx = 0, nLen = document.links.length; nIdx < nLen; document.links[nIdx++].onclick = processLink);
    }

    const

    /* customizable constants */
        sTargetId = "ajax-content", sViewKey = "view_as", sAjaxClass = "ajax-nav",

    /* not customizable constants */
        rSearch = /\?.*$/, rHost = /^[^\?]*\?*&*/, rView = new RegExp("&" + sViewKey + "\\=[^&]*|&*$", "i"), rEndQstMark = /\?$/,
        oLoadingBox = document.createElement("div"), oCover = document.createElement("div"), oLoadingImg = new Image(),
        oPageInfo = {
            title: null,
            scrollTop: {}, // NOT PART OF MDN EXAMPLE
            lowestSeenImage: {}, // NOT PART OF MDN EXAMPLE 
            url: location.href
        }, oHTTPStatus = /* http://www.iana.org/assignments/http-status-codes/http-status-codes.xml */ {
            100: "Continue",
            101: "Switching Protocols",
            102: "Processing",
            200: "OK",
            201: "Created",
            202: "Accepted",
            203: "Non-Authoritative Information",
            204: "No Content",
            205: "Reset Content",
            206: "Partial Content",
            207: "Multi-Status",
            208: "Already Reported",
            226: "IM Used",
            300: "Multiple Choices",
            301: "Moved Permanently",
            302: "Found",
            303: "See Other",
            304: "Not Modified",
            305: "Use Proxy",
            306: "Reserved",
            307: "Temporary Redirect",
            308: "Permanent Redirect",
            400: "Bad Request",
            401: "Unauthorized",
            402: "Payment Required",
            403: "Forbidden",
            404: "Not Found",
            405: "Method Not Allowed",
            406: "Not Acceptable",
            407: "Proxy Authentication Required",
            408: "Request Timeout",
            409: "Conflict",
            410: "Gone",
            411: "Length Required",
            412: "Precondition Failed",
            413: "Request Entity Too Large",
            414: "Request-URI Too Long",
            415: "Unsupported Media Type",
            416: "Requested Range Not Satisfiable",
            417: "Expectation Failed",
            422: "Unprocessable Entity",
            423: "Locked",
            424: "Failed Dependency",
            425: "Unassigned",
            426: "Upgrade Required",
            427: "Unassigned",
            428: "Precondition Required",
            429: "Too Many Requests",
            430: "Unassigned",
            431: "Request Header Fields Too Large",
            500: "Internal Server Error",
            501: "Not Implemented",
            502: "Bad Gateway",
            503: "Service Unavailable",
            504: "Gateway Timeout",
            505: "HTTP Version Not Supported",
            506: "Variant Also Negotiates (Experimental)",
            507: "Insufficient Storage",
            508: "Loop Detected",
            509: "Unassigned",
            510: "Not Extended",
            511: "Network Authentication Required"
        };

    var

        oReq, bIsLoading = false, bUpdateURL = false;

    oLoadingBox.id = "ajax-loader";
    oCover.onclick = abortReq;
    oLoadingImg.src = "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
    oCover.appendChild(oLoadingImg);
    oLoadingBox.appendChild(oCover);

    onpopstate = function (oEvent) {
        bUpdateURL = false;
        oPageInfo.title = oEvent.state.title;
        oPageInfo.scrollTop = oEvent.state.scrollTop; // NOT PART OF MDN EXAMPLE
        oPageInfo.lowestSeenImage = oEvent.state.lowestSeenImage; // NOT PART OF MDN EXAMPLE
        oPageInfo.url = oEvent.state.url;
        getPage();
    };

    window.addEventListener ? addEventListener("load", init, false) : window.attachEvent ? attachEvent("onload", init) : (onload = init);

    // Public methods

    this.open = requestPage;
    this.stop = abortReq;
    this.rebuildLinks = init;

})();
flowtron
  • 854
  • 7
  • 20