Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global OverlayScrollbars #485

Open
ferferga opened this issue Dec 6, 2022 · 11 comments
Open

Global OverlayScrollbars #485

ferferga opened this issue Dec 6, 2022 · 11 comments

Comments

@ferferga
Copy link

ferferga commented Dec 6, 2022

Is your feature request related to a problem? Please describe.
I want to have the same overlay scrollbars, with the same settings across my app, without having to take care of instantiating it manually.

Describe the solution you'd like
A deep key (with a boolean value) for the OverlayScrollbar instance options that chooses between adding the data-overlayscrollbars-initialize attribute and the overlayed scrollbars to every child (recursively) of the target element during the instantiation of the instance.

Describe alternatives you've considered
Creating my own Mutation Observer and doing all this process manually.

Additional context

  • An additional attribute data-overlayscrollbars-deep-ignore can be added so that element and its descendents are ignored, in case the user wants to instantiate a different OverlayScrollbars instance with a different set of options from the global one.

  • For frameworks like Vue, a directive could be used instead.

  • An additional option replaceGlobalAPIs to use in conjuction with deep that replace global APIs like window.scrollTo with functions that keep the same APIs intact, but call the scrolling to the global OverlayScrollbars instance would be nice.

@KingSora
Copy link
Owner

KingSora commented Dec 6, 2022

Good day @ferferga

Please describe your usecase specific usecase.. With your description I'm not sure whether this feature would make sense. Right now you could just do something like:

// you could use any selector you like
document.querySelectorAll('[data-overlayscrollbars-initialize]').forEach((elm) => {
  OverlayScrollbars(elm, {});
});

Which would initialize OverlayScrollbars to all of the matching elements.

If you are using a specific component framework (like Vue, React, etc.) you can also use the provided framework wrappers. They are available for:

And in case your framework is unsuported you could also write a component yourself. The code for that is really short and easy. (You can use the existing framework components as reference)

@ferferga
Copy link
Author

ferferga commented Dec 6, 2022

@KingSora That enables it for all the elements that match that attribute when the app starts, but not the elements with that attribute that could be created beforehand.

It doesn't dispose the instance if one of the elements that matches that attribute is removed from the DOM tree after the instance initialization.

As I mention in my issue, I can create my own Mutation Observer and do this manually, but I think it's a really common use case for people to just set OverlayScrollbars in body and expect every inner scrollbar to be an OverlayScrollbar one.

Although maybe I'm using this library in the wrong way: what I want to do is to have all the scrollbars in my page with OverlayScrollbars. Targeting body just renders an OverlayScrollbar in the body element, all the children element that needs scrollbars use native scrollbars.

@KingSora
Copy link
Owner

@ferferga sorry for letting you wait, had some work to do in the past weeks..

I understand your need for this feature but I think your usecase might not be as common as you think, at least not in the way you're describing it.. And since this code change would increase the size of the package by a substential amount let me explain why I most likely won't add it to the core package.

How was this problem approached until today?

Since you're the first one to request this feature, there must have been a solution or a different way to approach this problem. The usualy way of how such a plugin is initialized goes like this:

  1. The page loads
  2. You get all the dom elements you want the plugin be initialized to
  3. The plugin is initialized to those
  4. In case parts of the page are swapped / lazy loaded you repeat 2. and 3. for the affected part of the page

This flow should be always possible, since you usually know when those actions happen because something has to trigger them. You know when the page loads from the browser. Since you had to program any lazy loading or swapping of dom elements you also know when those things happen.

In case you are using react or any other component framework, the component is taking care of everything since it will automatically initialize the plugin on mount.

All of this leads me to the conclusion that a MutationObserver solution could be a great solution for the problem but it would come with several drawbacks which the flow described above wouldn't have:

  • Since the implementation is general, it would affect the whole page everytime something changes which could mean a performance penalty for DOM operations which wouldn't lead to a initialization of the plugin
  • The library would grow by a substential amount
  • The code needs to be maintained

Something which could be interesting for you though is a web component solution. I've thought about this myself and I'm open for publishing a separate overlayscrollbars-webcomponent (or something like that) package.

@ferferga
Copy link
Author

ferferga commented Dec 21, 2022

@KingSora No worries about the delay, we all have our lives :)

Let me summarise my reply in points:

  1. I'm impressed this wasn't asked by someone else before me, in fact I thoroughly looked before opening this issue because I thought I was doing something wrong. If I have an scrollbar with certain style, I think it's safe to assume end users expect the same style across all the page elements. It's basic UX and having full apps rendered by JS frameworks are so common nowadays that I assumed OverlayScrollbars took care of this automatically, even when using the raw package.

  2. I'm using Vue, but not the component, just the normal package in my main.ts file. I didn't see the need for the extra overhead both in bundle and runtime that a Vue component poses when I didn't need reactivity for the target element. I also couldn't find docs for properly running plugins with this version, which are a must for me. However, one question about this:

In case you are using react or any other component framework, the component is taking care of everything since it will automatically initialize the plugin on mount.

Before going with the manual OverlayScrollbar initialization approach I explained before, I tested the Vue component precisely to check if that helped with my issue. However, it didn't. Is that the expected behaviour for the framework components and we're dealing with a bug now?

In case it's not a bug and I simply misunderstood your sentence... Are you open for a PR in the Vue component to do this? Should be easy by watching component's $slots.

  1. My proposed solution in 2) doesn't cover my main concern, and it's the performance of duplicating multiple MutationObservers. What do you think of...:

    • Exposing OS's internal MutationObserver so it can be reused elsewhere
    • The ability to pass an instance of a MutationObserver when initializing a new OS instance, which can be reused if it's tracking an element above the target element of the initialised OS instance in the DOM tree.

    OR

    • The ability to pass an OS instance when initializing a new OS instance, so the new OS instance can reuse the state of the other one as much as possible (including MutationObserver). As with the first proposal, I guess this would require that the passed OS instance targets an element above the target element of the initialised OS instance in the DOM tree.

I didn't take a deeper look at the code and doesn't know the feasibility of my proposals, but hopefully these are the middle-ground we're aiming at?

Thank you very much in advance.

@KingSora
Copy link
Owner

@ferferga I belive there is a missunderstanding of what the Vue Component (or any other framework component does)..

Is your expectation that the Component tracks its children and applies the plugin to all child elements which have overflow?

@KingSora
Copy link
Owner

KingSora commented Mar 8, 2023

@ferferga anything new here? :)

@aleksey-hoffman
Copy link

aleksey-hoffman commented Mar 13, 2023

I'd love to have this as well. I have about 100 containers that are either always scrollable or can dynamically become scrollable.
Manually wrapping each container with <OverlayScrollbars> component would be quite inefficient.

@ferferga
Copy link
Author

@ferferga Yes, exactly that.

Apologize for the long time to reply, opened the notification and since it got marked as read I completely forgot! 😅

@KingSora
Copy link
Owner

KingSora commented Mar 31, 2023

@aleksey-hoffman @ferferga I'm open for creating a package which could help with your issues. Something like overlayscrollbars-observed (bad with names, I'm open for any suggestion). In that package you would have a function which requres a root element in the DOM. From there the logic would work like this:

  1. Every child element of this root element will be checked whether it is a element which should have OverlayScrollbars applied.
  2. A MutationObserver is applied to the root element so changes to the DOM are detected and step 1. can re-run as soon as a significant change happened.

The function would receive a set of options / configuration possibilities for customization and to improve performance.. such as:

  • Possibility to adjust / change the function which determines whether a element should initialize OverlayScrollbars.
  • Possibility to run a custom function for each element which initializes or destroys OverlayScrollbars to handle custom initialization parameters and logic.
  • Possibility to adjust the options of the MutationObserver.
  • Possibility to add a debounce / throttle so fast subsequent dom changes are handled as expected.

The function would return an object with a destroy function and other things like an array of instances, possibilities to add callbacks etc.


That beeing said, I would have one concern which relates to frameworks like Vue. Since this approach would change certain elements in the dom without Vue (or any other framework) knowing about it, there could be unexpected behavior. Since OverlayScrollbars adds its own DOM elements its certainly possible that they would just be removed by the frameworks because they don't know about them.

I made some tests for vue, react and angular:

Click on the + or - buttons to trigger a state change.

As you can see, sometimes in vue3 and react the unknown elements are removed when the state is changed and sometimes not. It often depends on certain factors which you can't really influence.

@aleksey-hoffman
Copy link

@KingSora good job on thinking it through.

Perhaps, the best solution would be to create framework specific plugins that would inject initialization into hooks.

It's quite a complex problem, I wouldn't want you to spend a huge amount of time on it, I'm not even sure if this is a popular problem. Perhaps wrapping all potentially scrollable elements manually wouldn't be that difficult, since I only need to do it once, even if there's 100 of them

@truedrug
Copy link

Hi Everyone, Really appreciate the work here. Is there any plans to add this feature?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants