react-scrollbars-custom

The best React custom scrollbars component

react-scrollbars-custom

× DEMO × LIVE EXAMPLE × CHANGELOG ×

  • Native browser scroll behavior - it don't emulate scrolling, only showing custom scrollbars, scrolling itself still native
  • Cross-browser and cross-platform - does not matter where and how, scrollbars looks the same everywhere
  • Ultimate performance - 60 FPS (using RAF) and highly optimised code
  • No extra stylesheets required - minimum inline styles out of the box or you can style it yourself however you want
  • Fully customizable - want a hippo as a scrollbar thumb? Well.. I don't judge you, you're free to do it!
  • Scrollbars nesting
  • Total tests coverage
  • Momentum scrolling for iOS
  • RTL support (read more)
  • Content sizes translation (read more)
  • Proper page zoom handling (native scrollbars does not show up)

INSTALLATION

npm install react-scrollbars-custom
# or via yarn
yarn add react-scrollbars-custom

INSTALLATION NOTE:
This lib is written in ES6+ and delivering with both, transpiled and untranspiled versions:

  • main field of package.json is pointing to transpiled ES3-compatible version with CJS modules resolution;
  • module field is pointing to transpiled ES3-compatible version with ES modules resolution;
  • esnext field is pointing to the ES6+ version with ES modules resolution;

Depending on your targets you may have to use Webpack and/or Babel to pull untranspiled version of package.
See some tips on wiring thing up: https://2ality.com/2017/06/pkg-esnext.html

USAGE

Underneath react-scrollbars-custom uses requestAnimationFrame loop, which check and update each known scrollbar, and as result - scrollbars updates synchronised with browser's render flow. The <Scrollbar /> component works out of the box, with only need of width and height to be set, inline or via CSS;

<Scrollbar style={{ width: 250, height: 250 }}>
  <p>Hello world!</p>
</Scrollbar>

Internet Explorer

react-scrollbars-custom is syntax-compatible with IE10, but you'll have to use polyfills - for example @babel/polyfill.

Styling

Probably you'll wish to customize your scrollbars on your own way via CSS - then simply pass noDefaultStyles prop - it will prevent all inline styling from appear.
But some of styles will remain due to their need for proper component work.

Native mode

One more pretty common need is to disable custom scrollbars and fallback to native ones, it can be done by passing native prop. It'll change the generated markup:

// scrollbar.scrollerElement
<div class="ScrollbarsCustom native trackYVisible trackXVisible">
  // scrollbar.contentElement - the one that holds tour content
  <div class="ScrollbarsCustom-Content">
    // YOUR CONTENT IS HERE
  </div>
</div>

As you see here - now the root element has the scrollerElement ref, but otherwise its treated as a before (as holder). contentElement behaves as it was before.

Content sizes translation

In some situations you may want to make the scrollbars block of variable sizes - just pass translateContentSize*ToHolder prop and component will automatically translate corresponding contentElement's sizes to the holderElement. If you are using default styles - it'll be handy to pass compensateScrollbarsWidth={false} props, to avoid infinite shrinking when it's not supposed to.
Note: This wont work for native mode.

RTL support

react-scrollbars-custom supports RTL direction out of the box, you don't have to pass extra parameters to make it work, it'll be detected automatically on first component's render. But you still able to override it through the prop. There are several things you have to know about:

  • Due to performance reasons direction autodetect happens is 3 cases:
    • On component mount;
    • On rtl property change;
    • On state isRtl set to non-boolean value;
  • rtl property has priority over the style or CSS properties;
  • If rtl prop has not been set (undefined) - direction will be detected automatically according to content element's CSS;
  • If rtl prop is true - direction: rtl; style will be applied to hte content element;
  • If rtl prop is false - no style will be applied to holder;

Customisation

In some cases you may want to change the default className or tagName of elements or add extra markup or whatever. For these purposes react-scrollbars-custom made fully customizable. You can do absolutely what ever you want y simply passing renderer SFC to the needed props.

IMPORTANT: Renderer will receive elementRef function that expect the DOM element's reference as first parameter.
Furthermore you have to pass the styles, cause they needed to proper component work.

<Scrollbar
  renderer={props => {
    const { elementRef, ...restProps } = props;
    return <span {...restProps} ref={elementRef} className="MyAwesomeScrollbarsHolder" />;
  }}
  wrapperProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="MyAwesomeScrollbarsWrapper" />;
    }
  }}
  scrollerProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="MyAwesomeScrollbarsScroller" />;
    }
  }}
  contentProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="Content" />;
    }
  }}
  trackXProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="TrackX" />;
    }
  }}
  trackYProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="trackY" />;
    }
  }}
  thumbXProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="ThUmBX" />;
    }
  }}
  thumbYProps={{
    renderer: props => {
      const { elementRef, ...restProps } = props;
      return <span {...restProps} ref={elementRef} className="tHuMbY" />;
    }
  }}
/>

Generated HTML

// scrollbar.holderElement
<div class="ScrollbarsCustom trackYVisible trackXVisible">
  // scrollbar.wrapperElement - the one that hiding native scrollbars
  <div class="ScrollbarsCustom-Wrapper">
    // scrollbar.scrollerElement - the one that actually has browser's scrollbars
    <div class="ScrollbarsCustom-Scroller">
      // scrollbar.contentElement - the one that holds tour content
      <div class="ScrollbarsCustom-Content">
        // YOUR CONTENT IS HERE
      </div>
    </div>
  </div>
  // scrollbar.trackYElement
  <div class="ScrollbarsCustom-Track ScrollbarsCustom-TrackY">
    // scrollbar.thumbYElement
    <div class="ScrollbarsCustom-Thumb ScrollbarsCustom-ThumbY" />
  </div>
  // scrollbar.trackXElement
  <div class="ScrollbarsCustom-Track ScrollbarsCustom-TrackX">
    // scrollbar.thumbXElement
    <div class="ScrollbarsCustom-Thumb ScrollbarsCustom-ThumbX" />
  </div>
</div>
  • If scrolling is possible or tracks are shown die to permanentScrollbar* prop - trackYVisible/trackXVisible classnames are applied to the holder element.
  • When thumb is dragged it'll have dragging classname.
  • If direction is RTL - rtl classname will be added to the holder element.
  • By default whole structure described above is rendered in spite of tracks visibility, but it can be changed by passing removeTrackXWhenNotUsed/removeTrackYWhenNotUsed/removeTracksWhenNotUsed props to the component. Respective tracks will not be rendered if it is unnecessary.

API

PROPS

You can pass any HTMLDivElement props to the component - they'll be respectfully passed to the holder element/renderer.

createContext :boolean = undefined
Whether to create context that will contain scrollbar instance reference.

rtl :boolean = undefined
true - set content's direction RTL, false - LTR, undefined - autodetect according content's style.

native :boolean = undefined
Do not use custom scrollbars, use native ones instead.

mobileNative :boolean = undefined
As native but enables only on mobile devices (actually when the scrollbarWidth is 0).

momentum :boolean = true
Whether to use momentum scrolling, suitable for iOS (will add -webkit-overflow-scrolling: touch to the content element).

noDefaultStyles :boolean = undefined
Whether to use default visual styles.
Note: Styles needed to proper component work will be passed regardless of this option.

disableTracksMousewheelScrolling :boolean = undefined
Disable content scrolling while preforming a wheel event over the track.

disableTrackXMousewheelScrolling :boolean = undefined
Disable content scrolling while preforming a wheel event over the track.

disableTrackYMousewheelScrolling :boolean = undefined
Disable content scrolling while preforming a wheel event over the track.

disableTracksWidthCompensation :boolean = undefined
Disable both vertical and horizontal wrapper indents that added in order to not let tracks overlay content. Note: Works only with default styles enabled.

disableTrackXWidthCompensation :boolean = undefined
Disable horizontal wrapper indents that added in order to not let horizontal track overlay content. Note: Works only with default styles enabled.

disableTrackYWidthCompensation :boolean = undefined
Disable vertical wrapper indents that added in order to not let vertical track overlay content. Note: Works only with default styles enabled.

minimalThumbSize :number = 30
Minimal size of both, vertical and horizontal thumbs. This option has priority to minimalThumbXSize/minimalThumbYSize props.

maximalThumbSize :number = undefined
Maximal size of both, vertical and horizontal thumbs. This option has priority to maximalThumbXSize/maximalThumbYSize props.

minimalThumbXSize :number = undefined
Minimal size of horizontal thumb.

maximalThumbXSize :number = undefined
Maximal size of horizontal thumb.

minimalThumbYSize :number = undefined
Minimal size of vertical thumb.

maximalThumbYSize :number = undefined
Maximal size of vertical thumb.

noScroll :boolean = undefined
Whether to disable both vertical and horizontal scrolling.

noScrollX :boolean = undefined
Whether to disable horizontal scrolling.

noScrollY :boolean = undefined
Whether to disable vertical scrolling.

permanentTracks :boolean = undefined
Whether to display both tracks regardless of scrolling ability.

permanentTrackX :boolean = undefined
Whether to display horizontal track regardless of scrolling ability.

permanentTrackY :boolean = undefined
Whether to display vertical track regardless of scrolling ability.

removeTracksWhenNotUsed :boolean = undefined
Whether to remove both vertical and horizontal tracks if scrolling is not possible/bocked and tracks are not permanent.

removeTrackYWhenNotUsed :boolean = undefined
Whether to remove horizontal track if scrolling is not possible/bocked and tracks are not permanent.

removeTrackXWhenNotUsed :boolean = undefined
Whether to remove vertical track if scrolling is not possible/bocked and tracks are not permanent.

translateContentSizesToHolder :boolean = undefined
Pass content's scrollHeight and scrollWidth values to the holder's height and width styles. Not working with native behavior.

translateContentSizeYToHolder :boolean = undefined
Pass content's scrollHeight values to the holder's height style. Not working with native behavior.

translateContentSizeXToHolder :boolean = undefined
Pass content's scrollWidth values to the holder's width style. Not working with native behavior.

trackClickBehavior :string = "jump"
The way scrolling behaves while user clicked the track:

  • jump - will cause straight scroll to the respective position.
  • step - will cause one screen step towards (like PageUp/PageDown) the clicked position.

scrollbarWidth :number = undefined
Scrollbar width value needed to proper native scrollbars hide. While undefined it is detected automatically (once per module require).
This prop is needed generally for testing purposes.

fallbackScrollbarWidth :number = 20
This value will be used in case of falsy scrollbarWidth prop. E.g. it is used for mobile devices, because it is impossible to detect their real scrollbar width (due to their absolute positioning).

scrollTop :number = undefined
Prop that allow you to set vertical scroll.

scrollLeft :number = undefined
Prop that allow you to set horizontal scroll.

scrollDetectionThreshold :number = 100
Amount of seconds after which scrolling will be treated as completed and scrollStop event emitted.

elementRef :function(ref: Scrollbar) = undefined
Function that receive the scrollbar instance as 1st parameter.

renderer :SFC = undefined
SFC used to render the holder. More about renderers usage.

wrapperProps :object = {}
Here you can pass any props for wrapper, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

contentProps :object = {}
Here you can pass any props for content, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

trackXProps :object = {}
Here you can pass any props for trackX, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

trackYProps :object = {}
Here you can pass any props for trackY, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

thumbXProps :object = {}
Here you can pass any props for thumbX, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

thumbYProps :object = {}
Here you can pass any props for thumbY, which is usually HTMLDivElement plus elementRef props which behaves as holder's elementRef prop.

onUpdate :function(scrollValues: ScrollValues, prevScrollValues: ScrollValues) = undefined
Function called each time any of scroll values changed and component performed an update. It is called after component's update.

onScroll :function(scrollValues: ScrollValues, prevScrollValues: ScrollValues) = undefined
Function called each time scrollTop or scrollLeft has changed. It is called after component's update and even if scrollTop/scrollLeft has been changed through the code (not by user).

onScrollStart :function(scrollValues: ScrollValues) = undefined
Callback that called immediately when user started scrolling (no matter how, thumb dragging, keyboard, mousewheel and etc.).

onScrollStop :function(scrollValues: ScrollValues) = undefined Callback that called after props.scrollDetectionThreshold milliseconds after last scroll event.

INSTANCE PROPERTIES

eventEmitter :Emittr
Event emitter that allow you to add events handler for cases when you access Scrollbars through context

holderElement :HTMLDivElement | null
Holder DOM element reference or null if element was not rendered

wrapperElement :HTMLDivElement | null
Wrapper DOM element reference or null if element was not rendered

scrollerElement :HTMLDivElement | null
Scroller DOM element reference or null if element was not rendered

contentElement :HTMLDivElement | null
Content DOM element reference or null if element was not rendered

trackXElement :HTMLDivElement | null
Horizontal track DOM element reference or null if element was not rendered

trackYElement :HTMLDivElement | null
Vertical track DOM element reference or null if element was not rendered

thumbXElement :HTMLDivElement | null
Horizontal thumb DOM element reference or null if element was not rendered

thumbYElement :HTMLDivElement | null
Vertical thumb DOM element reference or null if element was not rendered

(get|set) scrollTop :number
Content's element scroll top

(get|set) scrollLeft :number
Content's element scroll left

(get) scrollHeight :number
Content's element scroll height

(get) scrollWidth :number
Content's element scroll width

(get) clientHeight :number
Content's element client height

(get) clientWidth :number
Content's element client width

INSTANCE METHODS

getScrollState(force:boolean = false) :plain object Current scroll-related values, if force parameter is falsy - returns cached value which updated with RAF loop Returned values:

type ScrollState = {
  /**
   * @description Content's native clientHeight parameter
   */
  clientHeight: number;
  /**
   * @description Content's native clientWidth parameter
   */
  clientWidth: number;
  /**
   * @description Content's native scrollHeight parameter
   */
  scrollHeight: number;
  /**
   * @description Content's native scrollWidth parameter
   */
  scrollWidth: number;
  /**
   * @description Content's native scrollTop parameter
   */
  scrollTop: number;
  /**
   * @description Content's native scrollLeft parameter
   */
  scrollLeft: number;
  /**
   * @description Indicates whether vertical scroll blocked via properties
   */
  scrollYBlocked: boolean;
  /**
   * @description Indicates whether horizontal scroll blocked via properties
   */
  scrollXBlocked: boolean;
  /**
   * @description Indicates whether the content overflows vertically and scrolling not blocked
   */
  scrollYPossible: boolean;
  /**
   * @description Indicates whether the content overflows horizontally and scrolling not blocked
   */
  scrollXPossible: boolean;
  /**
   * @description Indicates whether vertical track is visible
   */
  trackYVisible: boolean;
  /**
   * @description Indicates whether horizontal track is visible
   */
  trackXVisible: boolean;
  /**
   * @description Indicates whether display direction is right-to-left
   */
  isRTL?: boolean;

  /**
   * @description Pages zoom level - it affects scrollbars
   */
  zoomLevel: number;
};

scrollToTop() :this
Scroll to the very top border of scrollable area

scrollToLeft() :this
Scroll to the very left border of scrollable area

scrollToBottom() :this
Scroll to the very bottom border of scrollable area

scrollToRight() :this
Scroll to the very right border of scrollable area

scrollTo(x?: number, y?: number) :this
Set the current scroll at given coordinates. If any value is undefined it'll be left as is.

centerAt(x?: number, y?: number) :this
Center viewport at given coordinates. If any value is undefined it'll be left as is.

HomePage

https://github.com/xobotyi/react-scrollbars-custom

Repository

https://github.com/xobotyi/react-scrollbars-custom.git


上一篇:@babel/plugin-transform-exponentiation-operator
下一篇:@babel/plugin-transform-property-literals

相关推荐

  • 🌈 React 函数式组件优化

    1. React 性能优化思路 减少重新 render 的次数。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

    4 天前
  • 🆘 一次理解清楚,为什么使用 React useEffect 中使用 setInterval 获取的值不是最新的

    Intro 这篇文章将通过一个使用 React Hook 常遇到的问题(stale state)入手,尝试理解 Hook 的内部运行逻辑。 废话不多说,直接看示例Sandbox。

    3 个月前
  • 高频数据交换下Flutter与ReactNative的对比

    (标题图片来自网络,侵删) 后端使用go写的socketio服务模拟期货行情数据,每10ms推送10条行情数据 ReactNative已经尽力优化了。 Flutter由于没flutter-socket...

    2 年前
  • 高性能迷你React框架 anu1.3.0 发布

    anujs1.3.0是一款高性能React-like框架,是目前世界上对React16兼容最好的迷你库。 自React16起,相继推出createContext,createPortal, creat...

    3 年前
  • 高德地图 react-amap 实战

    react-amap 是基于 React 的高德地图组件。 1. 获取地图示例 react-amap 作为高德地图在 React 中的实现,实际使用中不可避免的需要通过地图对象调用各种方法,reac...

    1 年前
  • 高品质 React UI 组件

    A high quality UI Toolkit, A Component Library for React 16+. 💘 Installation npm install isui --s...

    3 年前
  • 骚操作!在react中使用vuex

    原文地址 前言 笔者最近在学习使用react,提到react就绕不过去redux。redux是一个状态管理架构,被广泛用于react项目中,但是redux并不是专为react而生,两者还需要react...

    2 年前
  • 项目文档说明:react + Ant Design 的 blog-react-admin

    前言 此 blog-react-admin 项目是基于 蚂蚁金服开源的 ant design pro 之上,用 react 全家桶 + Ant Design 的进行再次开发的,项目已经开源,项目地址...

    2 年前
  • 面试题:Hooks 与 React 生命周期的关系

    React 生命周期很多人都了解,但通常我们所了解的都是 单个组件 的生命周期,但针对 Hooks 组件、多个关联组件(父子组件和兄弟组件) 的生命周期又是怎么样的喃?你有思考和了解过吗,接下来我们将...

    1 年前
  • 面试官:请你在React中引入Vue3的@vue/reactivity,实现响应式。

    前言 React的状态管理是一个缤纷繁杂的大世界,光我知道的就不下数十种,其中有最出名immutable阵营的redux,有mutable阵营的mobx,react-easy-state,在hooks...

    8 个月前

官方社区

扫码加入 JavaScript 社区