html - How to prevent scrolling beyond the safe area notch in React (iOSAndroid) with scrollIntoView? - Stack Overflow

I'm working on a feature where I need to scroll to a specific element in my React app, but I want

I'm working on a feature where I need to scroll to a specific element in my React app, but I want to prevent it from scrolling into the notch area on devices with a notch (iOS/Android). The idea is to make sure the scroll doesn't overlap with the "safe area" where the notch or system UI is located.

I have two versions of the scroll function: the initial version, which scrolls directly to the target, and a refactored version that attempts to adjust for the safe area using paddingTop from the app-bar element. However, my refactored solution is not working as expected. It still scrolls into the notch area on devices with one.

Here’s the code I have:

export const scrollToDiv = (htmlRef: RefObject<HTMLDivElement>, location: 'start' | 'nearest' | 'center' | 'end', behavior: 'auto' | 'smooth') => {
  setTimeout(() => {
    if (htmlRef?.current) {
      htmlRef.current.scrollIntoView({
        block: location,
        behavior: behavior,
      });
    }
  }, 200);
};

I'm working on a feature where I need to scroll to a specific element in my React app, but I want to prevent it from scrolling into the notch area on devices with a notch (iOS/Android). The idea is to make sure the scroll doesn't overlap with the "safe area" where the notch or system UI is located.

I have two versions of the scroll function: the initial version, which scrolls directly to the target, and a refactored version that attempts to adjust for the safe area using paddingTop from the app-bar element. However, my refactored solution is not working as expected. It still scrolls into the notch area on devices with one.

Here’s the code I have:

export const scrollToDiv = (htmlRef: RefObject<HTMLDivElement>, location: 'start' | 'nearest' | 'center' | 'end', behavior: 'auto' | 'smooth') => {
  setTimeout(() => {
    if (htmlRef?.current) {
      htmlRef.current.scrollIntoView({
        block: location,
        behavior: behavior,
      });
    }
  }, 200);
};

export const scrollToDiv = (htmlRef: RefObject<HTMLDivElement>, location: 'start' | 'nearest' | 'center' | 'end', behavior: 'auto' | 'smooth') => {
  setTimeout(() => {
    if (htmlRef?.current) {
      const element = htmlRef.current;
      const elementRect = element.getBoundingClientRect();

      // Safely get the app-bar element
      const appBar = document.getElementById('app-bar');
      
      // If the app-bar exists, get its padding-top; otherwise, default to 0
      const safeAreaTop = appBar ? parseInt(getComputedStyle(appBar).paddingTop) || 0 : 0;

      // Adjust the scroll to ensure the element is visible and doesn't overlap the safe area
      const adjustedTop = Math.max(elementRect.top - safeAreaTop, 0);

      // Scroll to the adjusted position, ensuring the element is fully visible above the safe area
      window.scroll({
        top: adjustedTop + window.scrollY, // This ensures we're considering the current scroll position
        behavior: behavior,
      });
    }
  }, 200);
};

Share Improve this question asked Mar 22 at 23:36 RichardsonRichardson 2,3141 gold badge24 silver badges62 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

The issue you’re facing occurs because the notch or safe area inset is not necessarily the same as the padding-top of your app bar. Instead, modern browsers on iOS/Android expose these safe areas using CSS environment variables like env(safe-area-inset-top).


Use env(safe-area-inset-top) in your CSS

Make sure your app bar or body element uses this safe area inset:

#app-bar {
  padding-top: env(safe-area-inset-top, 0px);
}

The fallback 0px ensures it works on devices without a notch. read more about this here


Adjust the scroll by reading the safe area inset from the computed style

In JavaScript, reading env(safe-area-inset-top) directly isn’t supported.

A reliable workaround is:

  • Add the inset via CSS on a known element (like #app-bar or <body>).
  • Parse the value from the computed style.

Here’s a corrected scroll function that accounts for both app-bar height and the safe area inset:

export const scrollToDiv = (
  htmlRef: RefObject<HTMLDivElement>,
  location: ScrollLogicalPosition = 'start',
  behavior: ScrollBehavior = 'smooth'
) => {
  setTimeout(() => {
    if (htmlRef?.current) {
      const element = htmlRef.current;
      const elementRect = element.getBoundingClientRect();
      const appBar = document.getElementById('app-bar');

      // Get both appBar height and safe-area-inset-top (if available)
      const appBarStyles = appBar ? getComputedStyle(appBar) : null;
      const appBarHeight = appBar ? appBar.getBoundingClientRect().height : 0;
      const safeAreaInsetTop = appBarStyles
        ? parseInt(appBarStyles.paddingTop.replace('px', '')) || 0
        : 0;

      // Calculate final offset
      const offset = appBarHeight + safeAreaInsetTop;

      // Scroll to the adjusted position
      window.scrollTo({
        top: window.scrollY + elementRect.top - offset,
        behavior: behavior,
      });
    }
  }, 200);
};

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744300957a4567498.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信