idiotWu / smooth-scrollbar

Customizable, Extendable, and High-Performance JavaScript-Based Scrollbar Solution.

Home Page:https://idiotwu.github.io/smooth-scrollbar/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

URL with hash links. With Firefox, it doesn't work properly.

mrWilson123 opened this issue · comments

Try the same thing with Firefox, it doesn't work properly.

Originally posted by @mrWilson123 in #360 (comment)

Any demo?

Sorry. Here is the demo: https://cdpn.io/pen/debug/RwjWbWG?authentication_hash=xnMabmDQpZBr#active

Code: https://codepen.io/mrWilson123/pen/RwjWbWG

Tested and works with browsers: Chrome, Opera, Edge. Can anyone please check this with Safari?

It does not work with Firefox properly. It messed up layouts and some parts are missing.

Thanks for the demo.

The problem with your code is that scrollbar.scrollIntoView() is called before the document is ready. You can use the following AnchorPlugin to fix it:

import { ScrollbarPlugin } from 'smooth-scrollbar';

export class AnchorPlugin extends ScrollbarPlugin {
  static pluginName = 'anchor';

  onHashChange = () => {
    this.jumpToHash(window.location.hash);
  };

  // remove this if you don't need hash links to work
  onClick = (event) => {
    const { target } = event;

    if (target.tagName !== 'A') {
      return;
    }

    const hash = target.getAttribute('href');

    if (!hash || hash.charAt(0) !== '#') {
      return;
    }

    this.jumpToHash(hash);
  };

  jumpToHash = (hash) => {
    const { scrollbar } = this;

    if (!hash) {
      return;
    }

    scrollbar.scrollIntoView(document.querySelector(hash), {
      offsetTop: -scrollbar.containerEl.scrollTop
    });
  };

  onInit() {
    this.jumpToHash(window.location.hash);

    window.addEventListener('hashchange', this.onHashChange);

    this.scrollbar.contentEl.addEventListener('click', this.onClick);
  }

  onDestory() {
    window.removeEventListener('hashchange', this.onHashChange);

    this.scrollbar.contentEl.removeEventListener('click', this.onClick);
  }
}

// usage
Scrollbar.use(AnchorPlugin);

Full-page demo: https://20rex.csb.app/#active
Source: https://codesandbox.io/s/anchor-plugin-smooth-scrollbar-20rex?file=/src/anchor-plugin.js

(I forked yours to codesandbox as I cannot open the debug view.)

Thank you for your reply, but how do I make it work with CodePen? I do not understand. I tried, but no luck. :(

I get every time "Uncaught SyntaxError: Cannot use import statement outside a module"

You can remove the import/export statement and use the global window.Scrollbar:

class AnchorPlugin extends Scrollbar.ScrollbarPlugin {
  static pluginName = 'anchor';

  onHashChange = () => {
    this.jumpToHash(window.location.hash);
  };

  // remove this if you don't need hash links to work
  onClick = (event) => {
    const { target } = event;

    if (target.tagName !== 'A') {
      return;
    }

    const hash = target.getAttribute('href');

    if (!hash || hash.charAt(0) !== '#') {
      return;
    }

    this.jumpToHash(hash);
  };

  jumpToHash = (hash) => {
    const { scrollbar } = this;

    if (!hash) {
      return;
    }

    scrollbar.scrollIntoView(document.querySelector(hash), {
      offsetTop: -scrollbar.containerEl.scrollTop
    });
  };

  onInit() {
    this.jumpToHash(window.location.hash);

    window.addEventListener('hashchange', this.onHashChange);

    this.scrollbar.contentEl.addEventListener('click', this.onClick);
  }

  onDestory() {
    window.removeEventListener('hashchange', this.onHashChange);

    this.scrollbar.contentEl.removeEventListener('click', this.onClick);
  }
}

// usage
Scrollbar.use(AnchorPlugin);

Still no luck. :( Please see my CodePen: https://codepen.io/mrWilson123/pen/podjEPP

You need to load plugins before initializing the scrollbar:

Scrollbar.use(AnchorPlugin);

Scrollbar.init(...);

It just works as expected in my environment (macOS 12.2, Firefox 96.0.3).

recording.mov

If it still doesn't work in your environment, you can try resetting the container.scrollTop:

  jumpToHash = (hash) => {
    const { scrollbar } = this;

    if (!hash) {
      return;
    }

    scrollbar.scrollIntoView(document.querySelector(hash), {
      offsetTop: -scrollbar.containerEl.scrollTop
    });

+   scrollbar.containerEl.scrollTop = 0;
  };

Strange that it didn't work for me. Firefox 96.0.3

But scrollbar.containerEl.scrollTop = 0; did the trick. Thank you very much. :)

But is it possible to add offset to it?

But scrollbar.containerEl.scrollTop = 0; did the trick.

Then I think resetting scrollTop before we call scrollbar.scrollIntoView() will work too. Can you test the following code in your environment?

  jumpToHash = (hash) => {
    if (!hash) {
      return;
    }

   const { scrollbar } = this;

    // reset scrollTop
    scrollbar.containerEl.scrollTop = 0;

    scrollbar.scrollIntoView(document.querySelector(hash));
  };

But is it possible to add offset to it?

Just use the offsetTop option:

scrollbar.scrollIntoView(document.querySelector(hash), {
  offsetTop: 1024
});

Yes, it works now. :)

To make it even better, could we add an offset through the data attributes? I mean something like this:

<nav class="menu">
  <ul>
    <li><a href="">Home</a></li>
    <li><a href="#section-1" data-offset="80">Section 1</a></li>
    <li><a href="#section-2" data-offset="100">Section 2</a></li>
    <li><a href="#section-3" data-offset="150">Section 3</a></li>
    <li><a href="#section-4" data-offset="200">Section 4</a></li>
    <li><a href="#section-5" data-offset="250">Section 5</a></li>
  </ul>
</nav>

There may be situations where different offsets need to be used.

Code: https://codepen.io/mrWilson123/pen/LYOpbRE

Simply change the code to:

  jumpToHash = (hash) => {
    if (!hash) {
      return;
    }

   const { scrollbar } = this;

    // reset scrollTop
    scrollbar.containerEl.scrollTop = 0;

    const target = document.querySelector(hash);

    if (target) {
      scrollbar.scrollIntoView(target, {
        offsetTop: parseFloat(target.getAttribute('data-offset')) || 0,
      });
    }
  };

Almost perfect. :)

One more thing. But what if the data attributes are not defined and we want to set the default offset?

But what if the data attributes are not defined and we want to set the default offset?

Change 0 to whatever you want.

I am going to close this issue as your questions have gone far beyond this project. If you want to learn the fundamentals of front-end web development, just google them.

Thank you very much for your time. I think this is an awesome plugin. :)

Here is a working code sample in case someone else needs it: https://codepen.io/mrWilson123/pen/OJOyaGG

Demo: https://cdpn.io/pen/debug/OJOyaGG?authentication_hash=dGrXWjvQdvBM