docsifyjs / docsify

🃏 A magical documentation site generator.
https://docsify.js.org
MIT License
27.44k stars 5.67k forks source link

Direct link to sub-item not scrolling to correct position #351

Open adrianbj opened 6 years ago

adrianbj commented 6 years ago

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.

QingWei-Li commented 6 years ago

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

adrianbj commented 6 years ago

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

adrianbj commented 5 years ago

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

cheng-kang commented 5 years ago

@adrianbj just raised a PR for this issue: https://github.com/docsifyjs/docsify/pull/723

Would be great if you could help testing it 😄

IssueHuntBot commented 5 years ago

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

rizdaprasetya commented 5 years ago

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 ¯_(ツ)_/¯

Chimildic commented 3 years ago

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);
    }
})();
iammerrick commented 3 years ago

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);
zhangtian512 commented 8 months ago

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 ¯(ツ)

worked for me. thanks

stdword commented 2 months ago

Hello! In 2024 this issue still exists for plugins. I've faced this one in docsify-tabs. And agree with @jhildenbiddle — it should be handled by docsify with something like:

hook.beforeScrollToAnchor(function (path, anchor) {
    // detect the height of the content and calculate the correct position for scrolling
});

Here is my solution for docsify-tabs. And I combined it with my version of scroll-finisher:

function fixTabs() {
  const uriComponent = window.location.hash || window.location.search;
  const anchorID = (uriComponent.match(/(?:id=)([^&]+)/) || [])[1]
  if (!anchorID)
    return

  const anchorElement = document.querySelector('#' + decodeURIComponent(anchorID));

  function adjust(treshold = 3, interval = 20, pixelDelta = 4) {
    if (adjust.state !== null) {
      if (window.pageYOffset == adjust.state) {
        adjust.settledCount++
        if (adjust.settledCount > treshold) {
          if (Math.abs(window.pageYOffset - anchorElement.offsetTop) > pixelDelta)
            window.scrollTo({top: anchorElement.offsetTop, behavior: 'smooth'});
          else return
        }
      }
    }

    if (adjust.state === null || adjust.settledCount <= treshold)
      setTimeout(adjust, interval)
    adjust.state = window.pageYOffset
  }
  adjust.settledCount = 0
  adjust.state = null

  if (anchorElement)
    adjust()
}

function docsifyTabsFix(hook, vm) {
  hook.doneEach(function () {
    const hasTabs = document.querySelector(`.docsify-tabs__tab`);
    if (hasTabs)
      fixTabs()
  });
}

window.$docsify = window.$docsify || {};
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], docsifyTabsFix);