docsifyjs/docsify

Direct link to sub-item not scrolling to correct position

adrianbj opened this issue · 8 comments

I am sure I must have some setting incorrect, but the first time I load this:
https://adrianbj.github.io/TracyDebugger/#/debug-bar?id=module-disabler

it has the "module-disabler" section at the bottom of the page. I browser reload brings it to the top as expected.

However I don't see the problem with the docs page for docsify, eg: https://docsify.js.org/#/configuration?id=load-navbar works as expected on the first load.

Am I doing something incorrectly?

Thanks, and I hope this is the best place to ask for help.

Because there is a picture on your page, the picture height is unknown at first load.

Any chance you could move the scroll feature code to be triggered after all images have been loaded?

Hi @QingWei-Li - any chance I can get this back on your radar please?

@adrianbj just raised a PR for this issue: #723

Would be great if you could help testing it 😄

@issuehuntfest has funded $40.00 to this issue. See it on IssueHunt

Took quite a while to wait proposed PR to be merged, I end up creating a plugin to overcome this issue
Just include this script tag like the other default Docsify plugins (search, emoji, etc). Put it after Docsify script tag.

<script src="https://cdn.jsdelivr.net/gh/rizdaprasetya/docsify-fix-pageload-scroll@master/index.js"></script>

or add this on your $docsify.plugins declaration:

function (hook, vm) {
    hook.ready(function () {
        // true = show debug log
        let dd = false 
        let TARGET_QUERY = 'id'
        let SCROLL_DELAY = 2000 // in milisecond
        let location = window.location

        dd&&console.log('custom scroll plugin called!')
        let currentUrlWithoutHash = new URL(
            location.origin+location.pathname+
            location.search+location.hash.substring(1)
        )
        let urlQueryParam = currentUrlWithoutHash.searchParams
        let isUrlHasIdQuery = urlQueryParam.has(TARGET_QUERY)
        if(isUrlHasIdQuery){
            dd&&console.log('url has id, will scroll to element')
            let urlId = urlQueryParam.get(TARGET_QUERY)
            // run delayed, to make sure everything loaded
            setTimeout(function() {
                dd&&console.log('will scroll now!')
                try{
                    document.querySelector('#'+urlId)
                        .scrollIntoView()
                } catch(e){ dd&&console.log('custom scroll failed',e) }
            }, SCROLL_DELAY);
        }
    })
},

Incase anyone interested.
Ugly maybe, but it simply works for me ¯_(ツ)_/¯

Works for me

// index.html
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<script src="./script/docsify-fix-pageload-scroll.js"></script>
// docsify-fix-pageload-scroll.js
(function () {
    function create() {
        return (hook) => {
            const TARGET_QUERY = 'id';
            const SCROLL_DELAY = 500;

            hook.doneEach(function () {
                if (!location.hash.includes('?')) return;
                let searchParams = new URLSearchParams(location.hash.split('?')[1]);
                let header = document.querySelector('#' + searchParams.get(TARGET_QUERY));
                header && setTimeout(() => header.scrollIntoView(), SCROLL_DELAY);
            });
        };
    }

    if (typeof $docsify === 'object') {
        $docsify.plugins = [].concat(create(), $docsify.plugins);
    }
})();

I solved this problem a bit differently, instead of waiting for a certain amount of time, I instead check the top offset of the targeted element. Once the top offset value is "settled" I scroll it into view. All of this feels hacky as can be to me but alas! :-D

(function (win) {
  function create() {
    const urlId = new URLSearchParams(win.location.search).get("id");
    if (!urlId) return null;

    return function (hook, vm) {
      const threshold = 20;
      let identical = 0;
      let lastCheck = 0;

      hook.ready(function () {
        const element = win.document.getElementById(urlId);
        (function poll() {
          var elDistanceToTop =
            window.pageYOffset + element.getBoundingClientRect().top;

          if (lastCheck === elDistanceToTop) {
            identical++;
          }

          lastCheck = elDistanceToTop;

          if (identical > threshold) {
            element.scrollIntoView();
          } else {
            setTimeout(poll, 10);
          }
        })();
      });
    };
  }

  win.ScrollAfterImageLoad = {};
  win.ScrollAfterImageLoad.create = create;

  if (typeof win.$docsify === "object") {
    win.$docsify.plugins = [].concat(create(), $docsify.plugins);
  }
})(window);