KingSora / OverlayScrollbars

A javascript scrollbar plugin that hides the native scrollbars, provides custom styleable overlay scrollbars, and preserves the native functionality and feel.
https://kingsora.github.io/OverlayScrollbars
MIT License
3.71k stars 215 forks source link

Native scrollbar is visible on iOS #622

Closed dannybeckett closed 3 months ago

dannybeckett commented 3 months ago

Describe the bug On a physical iPhone, iOS's native scrollbar is visible when scrolling the page. This bug also affects the demo page.

To Reproduce

  1. Launch Safari
  2. Open the demo page
  3. Observe that the scrollbars look correct - the native scrollbars are not visible: IMG_5116
  4. Tap and drag somewhere on the page to natively scroll
  5. Observe that the native scrollbars now overlap the OverlayScrollbars: IMG_5117

Expected behavior Native scrollbars should not be visible

Examples Demo page

Environment

Additional context

Related https://github.com/KingSora/OverlayScrollbars/issues/545

KingSora commented 3 months ago

Good day @dannybeckett

Unfortunately this is a limitation of Safari. Technically the scrollbars are hidden (thats why the scrollbars are not visible in the inner square box), but the "viewport indicators" which are also shown when you just zoom in into the page are not. This also affected chrome and other browsers on mobile until they supported the scrollbar-width css property. Safari doesn't support the css property even in version 17. (https://caniuse.com/?search=scrollbar-width)

The workaround here is to cancel the initialization of the plugin in this case:

OverlayScrollbars({
  target: document.body,
  cancel: {
    nativeScrollbarsOverlaid: true,
  },  
});
dannybeckett commented 3 months ago

Ah I see @KingSora

If I cancel the init then the custom scrollbars won't show, right?

In which case it's probably still preferable to keep it showing both native scrollbars + custom scrollbars, since Safari hides the native scrollbar except on scroll (thus making it unclear that there is more content beneath).

KingSora commented 3 months ago

@dannybeckett If you cancel the initialization the custom scrollbars won't show and all related attributes like data-overlayscrollbars-initialize are removed in case they are applied

dannybeckett commented 3 months ago

Thanks for your help @KingSora - I'll leave it alone; hopefully Apple might eventually add support - one can hope xD

KingSora commented 3 months ago

We all hope :D

dannybeckett commented 3 months ago

Just to state the obvious, wrapping the contents of <body> in a container <div> is another workaround:

<html>
    <body>
        <div class="body-content" data-overlayscrollbars-initialize>
            <div id="something">Your actual content here</div>
            <div id="another">Continued...</div>
        </div>
    </body>
</html>

Also set it to use .body-content rather than body in:

const scrollbar_content = OverlayScrollbars(document.querySelector('.body-content'), {});

For me I also needed the following styles:

div.body-content {
  height: 100%;
  left: 0;
  position: absolute;
  top: 0;
  width: 100%;
}

Now the <body> has a scrollbar without iOS' native scrollbars overlapping :)

KingSora commented 3 months ago

@dannybeckett THanks for this suggestion!

Thats also a valid workaround but has other drawbacks to be aware of:

dannybeckett commented 3 months ago

@dannybeckett Thanks for this suggestion!

Thats also a valid workaround but has other drawbacks to be aware of:

  • on mobile this will prevent the url bar to hide / show depending on your scrolling
  • on mobile pull to refresh will not work anymore
  • will alter the functionality of window.scrollTo and other scroll apis related to the window global

I have just tested on Safari for iPhone and thankfully I'm not seeing any issues on our site.

The URL bar does fail to hide/show like you mentioned, but pull-to-refresh still works and our #anchor links still jump to the correct section.

We aren't using window.scrollTo in our JS but we are using document.getElementById('someId').scrollIntoView() (in seperate code to the anchor links) and it doesn't seem to affect that.

I'm very happy to have found a nice workaround, thank you so much for creating such an awesome piece of kit!

dannybeckett commented 3 months ago

@KingSora So after posting that, I've spent several hours working out the fix for a "gotcha" that I've found when using my workaround above. On Chrome for Windows the arrow up/down keyboard keys do not work unless you click somewhere in the middle of the page first. On an iPad with an Apple Magic Keyboard attached, no amount of clicking the middle of the page will enable the up/down arrow keyboard keys.

I was able to workaround this by changing the stylesheet to:

html, body, div.body-content {
  height: 100%;
  width: 100%;
}

(Removing position: absolute; as well as left: 0; top: 0; and also applying the sizes to html and body)

Then adding tabindex="-1" to a div within .body-content but not on .body-content itself (tabindex denotes that this element is focusable, and -1 denotes that it can only be focused on programmatically and not by the user).

Then focus this inner <div> on page load & when the scrollbar is clicked:

function refocus() {
  document.getElementsByClassName('some-inner-div')[0].focus();
}

window.addEventListener('load', function() {
  refocus();

  var classNames = ['os-scrollbar-track', 'os-scrollbar-handle'];
  for(var i = 0; i < classNames.length; i++) {
    var elements = document.getElementsByClassName(classNames[i]);
    for(var j = 0; j < elements.length; j++) {
      document.getElementsByClassName(classNames[i])[j].addEventListener('mouseup', refocus);
    }
  }
});

This fixes the up/down arrow keys on both Chrome for Windows + Safari for iPad, but leaves a black border around the div on Windows - which you can remove with:

div.some-inner-div:focus {
  outline: none;
}

The last thing that needs to be done is to set data-overlayscrollbars-initialize on html + body + div.body-content to remove the flickering of the OS-native scrollbars.

KingSora commented 3 months ago

@dannybeckett I didn't mention it before because I thought you didn't have any issues, but OverlayScrollbars is able to do the initialization you did out of the box (with all the focus management etc.):

OverlayScrollbars({
  target: document.body,
  elements: {
    viewport: false,
  },
  cancel: {
    body: false,
  },
});
dannybeckett commented 3 months ago

@KingSora Sorry to bother you again! I tried your code above on my site (after undoing the things I did before, I.E. adding the container <div>, JS focus + CSS).

The custom scrollbars are visible, and the iOS-native scrollbars no longer overlap, but unfortunately it isn't focusing.

On Chrome for Windows: I have to click somewhere in the middle of the page to get the arrow up/down keyboard keys to work.

On Safari for iPad with a physical keyboard attached: interestingly I don't have to tap the screen, but I can only press the arrow down key once. After that you are unable to press either the up or down arrow keys. However scrolling by touch dragging the middle of the screen immediately works after.

On the other hand, if you don't press the arrow down key on the iPad first, then trying to touch-drag the middle of the page to scroll it fails for about the first 5 attempts. The page does not scroll.

Am I able to specify which element should receive focus? In my example that would be div.some-inner-div, which is a few levels deep within <body>.

Any ideas please? I will admit I am not 100% sure what the target, viewport and cancel body commands together are doing exactly. Sorry for being a pain!

Thanks a lot!

KingSora commented 3 months ago

@dannybeckett OverlayScrollbars will automatically focus its viewport element, not your custom element... can you please post an small example on StackBlitz with your setup? I then can take a look what could be done

KingSora commented 3 months ago

@dannybeckett I've just published v2.7.1 which includes fixes to your issue... please try it out and give feedback :)

dannybeckett commented 3 months ago

Wow @KingSora I am incredibly impressed with the speed of your response - thank you for being so dedicated!

Needless to say that 2.7.1 is perfect in every way, it has truly hit the nail right on the head! No more of my yucky workarounds! 🥳😆

Seriously, great job! Thank you so much once again!!

All the best

KingSora commented 3 months ago

@dannybeckett glad I could help you :)