Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I scroll a div to be visible in ReactJS?

I have a popup list which is a div that contains a vertical list of child divs. I have added up/down keyboard navigation to change which child is currently highlighted.

Right now, if I press the down key enough times, the highlighted item is no longer visible. The same thing also occurs with the up key if the view is scrolled.

What is the right way in React to automatically scroll a child div into view?

like image 490
MindJuice Avatar asked May 27 '15 23:05

MindJuice


People also ask

How do I give a scroll to a div in React?

You can apply it by placing the overflow-y:scroll in the id growth inside the style tag. Notice the scroll bar on the right side of the div .

How do I scroll a specific div?

The scrollTo method: The scrollTo() is used to scroll to the specified element in the browser. Syntax: Here, x-cord specifies the x-coordinate and y-cord specifies the y-coordinate. Example: Using scrollTo() to scroll to an element.

How get scroll position of DIV in React?

To get scroll position with React, we can add a scroll listener to window . to create the scrollPosition state with the useState hook. Then we add the handleScroll function that takes the scroll position with the window.


2 Answers

I assume that you have some sort of List component and some sort of Item component. The way I did it in one project was to let the item know if it was active or not; the item would ask the list to scroll it into view if necessary. Consider the following pseudocode:

class List extends React.Component {   render() {     return <div>{this.props.items.map(this.renderItem)}</div>;   }    renderItem(item) {     return <Item key={item.id} item={item}                  active={item.id === this.props.activeId}                  scrollIntoView={this.scrollElementIntoViewIfNeeded} />   }    scrollElementIntoViewIfNeeded(domNode) {     var containerDomNode = React.findDOMNode(this);     // Determine if `domNode` fully fits inside `containerDomNode`.     // If not, set the container's scrollTop appropriately.   } }  class Item extends React.Component {   render() {     return <div>something...</div>;   }    componentDidMount() {     this.ensureVisible();   }    componentDidUpdate() {     this.ensureVisible();   }    ensureVisible() {     if (this.props.active) {       this.props.scrollIntoView(React.findDOMNode(this));     }   } } 

A better solution is probably to make the list responsible for scrolling the item into view (without the item being aware that it's even in a list). To do so, you could add a ref attribute to a certain item and find it with that:

class List extends React.Component {   render() {     return <div>{this.props.items.map(this.renderItem)}</div>;   }    renderItem(item) {     var active = item.id === this.props.activeId;     var props = {       key: item.id,       item: item,       active: active     };     if (active) {       props.ref = "activeItem";     }     return <Item {...props} />   }    componentDidUpdate(prevProps) {     // only scroll into view if the active item changed last render     if (this.props.activeId !== prevProps.activeId) {       this.ensureActiveItemVisible();     }   }    ensureActiveItemVisible() {     var itemComponent = this.refs.activeItem;     if (itemComponent) {       var domNode = React.findDOMNode(itemComponent);       this.scrollElementIntoViewIfNeeded(domNode);     }   }    scrollElementIntoViewIfNeeded(domNode) {     var containerDomNode = React.findDOMNode(this);     // Determine if `domNode` fully fits inside `containerDomNode`.     // If not, set the container's scrollTop appropriately.   } } 

If you don't want to do the math to determine if the item is visible inside the list node, you could use the DOM method scrollIntoView() or the Webkit-specific scrollIntoViewIfNeeded, which has a polyfill available so you can use it in non-Webkit browsers.

like image 162
Michelle Tilley Avatar answered Sep 19 '22 09:09

Michelle Tilley


For React 16, the correct answer is different from earlier answers:

class Something extends Component {   constructor(props) {     super(props);     this.boxRef = React.createRef();   }    render() {     return (       <div ref={this.boxRef} />     );   } } 

Then to scroll, just add (after constructor):

componentDidMount() {   if (this.props.active) { // whatever your test might be     this.boxRef.current.scrollIntoView();   } } 

Note: You must use '.current,' and you can send options to scrollIntoView:

scrollIntoView({   behavior: 'smooth',   block: 'center',   inline: 'center', }); 

(Found at http://www.albertgao.xyz/2018/06/07/scroll-a-not-in-view-component-into-the-view-using-react/)

Reading the spec, it was a little hard to suss out the meaning of block and inline, but after playing with it, I found that for a vertical scrolling list, block: 'end' made sure the element was visible without artificially scrolling the top of my content off the viewport. With 'center', an element near the bottom would be slid up too far and empty space appeared below it. But my container is a flex parent with justify: 'stretch' so that may affect the behavior. I didn't dig too much further. Elements with overflow hidden will impact how the scrollIntoView acts, so you'll probably have to experiment on your own.

My application has a parent that must be in view and if a child is selected, it then also scrolls into view. This worked well since parent DidMount happens before child's DidMount, so it scrolls to the parent, then when the active child is rendered, scrolls further to bring that one in view.

like image 32
eon Avatar answered Sep 23 '22 09:09

eon