I'm trying to implement on click scroll to top in react, but since I'm using custom scrollbar 2 I couldn't able to achieve it. The pageYoffset value is always 0 since I made the parent component max-height as 100vh and child component as scrollable. Here is what I got till now
<Scrollbars onScrollFrame={handleScroll}>
<ChildComponent/>
{
showButton && <button onClick={handleClick}><button/>
}
</Scrollbars>
In handleScroll
const {top} = values;
if(top > 0.02){
setShowButton(true);
}else{
setShowButton(false);
}
Depending on the top value I can show the button now I want to implement the scroll function when the button got clicked and scroll to the child component top
In handleClick I've tried to scroll it to top
window.scrollTo({ top: 0, behavior: "smooth" });
I've tried scrollIntoView with the top element as well it's not working.
Related
I am trying to implement a custom pull to refresh indicator using a Flatlist at some point i need the list to scroll at negative position to show the indicator at the top but scrollToOffset scrolls me to the top instead to y = 0
handleRelease() {
if (this.state.readyToRefresh) {
this.flatList.scrollToOffset({ offset: -130 });
}
}
<FlatList
ref={(flatList: any) => {
this.flatList = flatList;
}}
data={this.props.data}
renderItem={renderRowItem}
onScroll={this.handleScroll}
onResponderRelease={this.handleRelease}
scrollEventThrottle={16}
/>
Non-negative values seems working fine
It turns out you need to set scrollToOverflowEnabled to true to apply this behavior
ScrollView
When true, the scroll view can be programmatically scrolled beyond its
content size. The default value is false.
I haven't seen a thread that handles this, at least not for React.
My case: I want to conditionally render a back to top button only when scrolling is an option. It makes no sense to have such a feature if it can't affect the page.
The only solutions I can find are in jQuery. I'm using react-scroll but couldn't find any functionality there for this.
When a scrollbar is visible then window.visualViewport.width < window.Width.
var buttonIsVisible = window.visualViewport.width < window.Width;
To check if scrollbar is visible in vertical appearance.
document.body.clientHeight > window.innerHeight
I added this code in a useEffect.
useEffect(() => {
if (document.body.clientHeight > window.innerHeight) {
something()
}
}, [state]);
Luke.
By "scrolling is an option" I am assuming here that you mean "when the scrollbar is visible."
As far as I am aware, there is not any way to check for scrollbar visibility using the React API. There is the DOM boolean window.scrollbars.visible; however, I have not had luck with this. It seems to always return true whether a scrollbar is visible or not. The following approach may work for you:
You could set an event listener for onScroll and check window.scrollY. If window.scrollY > 0, then you could conditionally render the button. If window.scrollY is 0, then the page is already scrolled to the top and there is no need to display the button.
Depending on the design of your web app, checking once for scrollbar visibility (e.g., on componentDidMount) may not be the best option, since some DOM elements may continue to load after the component initially mounts and the height of the page may change.
I hope this is helpful.
If you have a wrapper around the element that has the scroll you can detect the width difference.
<div className="wrapper">
<div className="scrollingContent">
Very long content here
</div>
</div>
const scrollBarWidth = this.wrapper.clientWidth - this.scrollingContent.clientWidth;
this.setState({ scrollBarWidth });
Most of the time (depending on edge cases where elements are sized differently). You can use an element ref to check if the scrollWidth is greater than the current width (or height for vertical scroll). The ref might not update scroll properties with useEffect hence why you need state in the dependencies array. Plus you will likely want to add a window resize event listener to run the same code.
const ref = useRef(null);
const [hasScrollBar, setHasScrollBar] = useState(false);
useEffect(() => {
function updateState() {
const el = ref.current;
el && setHasScrollBar(el.scrollWidth > el.getBoundingClientRect().width);
}
updateState();
window.addEventListener('resize', updateState);
return () => window.removeEventListener('resize', updateState);
}, [state]);
<div ref={ref} style={{ overflowX: 'auto' }}>
{state}
</div>
I have an overlayed view in my React Native app which I need to animate on and off screen when the user pushes a button. I know how to position the view and animate it but I can't work out how to trigger the animation.
My redux store has a very simple state with an isOpen flag saying whether the panel is open or closed. I map the state to the panel component's props and when the isOpen prop changes I want to trigger the open or close animation. Obviously if the user presses the toggle button mid animation the currently running animation needs to be cancelled.
This should be simple but I can't find any examples. Any help would be much appreciated.
React Native
To begin an animation on a change of props you can simply start your animation in componentDidUpdate. Here's an example:
componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.state.animation.start();
}
}
Assuming your animation is defined in the component's state.
React (Browser):
[Not relevant to this question but potentially useful.]
A simple way to do this is using CSS transitions. What you can do is give the panel component a CSS class for closed (or open but I find using the closed/collapsed as a style easier because then the default is open).
Then in your panel's render:
render() {
const { isOpen } = this.props;
return <div className={ 'panel' + (isOpen ? '' : ' closed') }></div>
}
And in your CSS:
.panel {
/* some other styles */
transition: .5s ease-in;
}
.closed {
height: 0;
}
This way CSS can handle the animation logic and your concern of clicking the open/close button before the current animation has finished is addressed.
Here are the CSS transition docs: https://developer.mozilla.org/en-US/docs/Web/CSS/transition
Edit: A disadvantage of this method is that height must be explicitly set when the panel is open.
Here's a little example snippet:
function togglePanel() {
const panel = document.querySelector('div.panel');
if (panel.classList.contains('closed')) {
panel.classList.remove('closed');
} else {
panel.classList.add('closed');
}
}
.panel {
background-color: #00c0de;
height: 4rem;
overflow: hidden;
transition: .5s ease-in;
}
.closed {
height: 0;
}
<button onclick='togglePanel()'>Toggle Panel</button>
<div class='panel closed'>
<span>Hello, I'm the panel</span>
</div>
A new update to SwipeableViews allows for each Tab in the MaterialUI Tabs Component to have their own height. Let me back up a little. Before, MaterialUI Tabs component will show all tabs in the height of the longest tab. This created unnecessary scrolling on tabs that didn't need it. SwipeableViews recently fixed this by adding this to their component
<SwipeableViews
action={actions => {this.swipeableActions = actions;}}>
<div>{'slide n°1'}</div>
<div>{'slide n°2'}</div>
<div>{'slide n°3'}</div>
</SwipeableViews>
and
componentDidUpdate() {
this.swipeableActions.updateHeight();
}
That fixed the height issue across tabs on their loaded state. But when items are hidden and shown with a click event, the height persists and shown items do not show (being cut off from view)
See image (imgur failed to load the image) see the image link instead
image
You need to add:
animateHeight
to the SwipeableViews component.
You may also have to pass a function down to child components in order to update the height when a child modifies the view.
UpdateSwipeableViewHeight = () => {
if (this.swipeableActions) this.swipeableActions.updateHeight()
};
<SwipeableViews
id="searchTabsSwipeableView"
axis={theme.direction === 'rtl' ? 'x-reverse' : 'x'}
index={activeTabIndex}
onChangeIndex={index => {
this.handleChange(null, index);
this.UpdateSwipeableViewHeight();
}
}
action={actions => (this.swipeableActions = actions)}
animateHeight
>
<div className={classes.swipableViewsComponent}>
<ChildComponent updateHeight={this.UpdateSwipeableViewHeight} />
</div>
I'm using react and trying to build a facebook like chat - where the right column scrolls down and has a list of threads, and the middle column had the thread content and scrolls bottom up.
I have it all setup using flex, but am stuck on getting the middle column to scroll to the bottom by default (since I want to see the latest chat message). Here is the snippet for the actual container (I'm using React bootstrap):
<Row >
<Col ref={ (node) => { this.cont = node }}>
{this._buildThread()};
</Col>
</Row>
and here is the snippet for my ComponentDidMount:
componentDidMount() {
MessageStore.addChangeListener(this._onChange);
MessageService.getThread(this.props.id, 1000, 1, false);
this.cont.scrollTop = this.cont.scrollHeight;
}
Where cont is the ref for the container div that holds my messages. Yet nothing happens - it doesn't scroll, and if I look at node.scrollTop after setting it, it remains at 0 - seems immutable.
Any help would be much appreciated. Thanks!
Not sure about the details of your CSS, but I just had a similar-looking issue. The solution in my case was to make sure that the element itself would scroll and not its container:
.containerIWantToScroll {
overflow-y: auto;
}
If the element's container expands to fit the element, then that element will not scroll, thus its scrollTop value is always zero.
When are you triggering the code you included? To scroll to the bottom on render, I would write something like the following, calling triggerScroll in componentDidMount:
class App extends React.Component {
componentDidMount () {
this.triggerScroll();
}
triggerScroll () {
this.cont.scrollTop = this.cont.scrollHeight;
}
render () {
return (
<div>
<div className='containerIWantToScroll' ref={ (node) => { this.cont = node } }>
Content
</div>
</div>
)
}
}