Javascript 如何滚动 div 以在 ReactJS 中可见?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30495062/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:16:52  来源:igfitidea点击:

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

javascripthtmlcssscrollreactjs

提问by MindJuice

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

我有一个div包含 childdiv的垂直列表的弹出列表。我添加了向上/向下键盘导航以更改当前突出显示的子项。

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 divinto view?

React 中自动将孩子滚动div到视图中的正确方法是什么?

回答by Michelle Tilley

I assume that you have some sort of Listcomponent and some sort of Itemcomponent. The way I did it in one projectwas 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:

我假设您有某种List组件和某种Item组件。我在一个项目中的做法是让项目知道它是否处于活动状态;如有必要,该项目会要求列表将其滚动到视图中。考虑以下伪代码:

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 refattribute to a certain item and find it with that:

更好的解决方案可能是让列表负责将项目滚动到视图中(项目不知道它甚至在列表中)。为此,您可以ref为某个项目添加一个属性并使用该属性找到它:

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 availableso you can use it in non-Webkit browsers.

如果您不想通过数学运算来确定该项目是否在列表节点内可见,您可以使用DOM 方法scrollIntoView()或特定于 Webkit 的方法scrollIntoViewIfNeeded,它具有可用的 polyfill,因此您可以在非 Webkit 浏览器中使用它。

回答by eon

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

对于 React 16,正确答案与之前的答案不同:

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:

注意:您必须使用“.current”,并且您可以向 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/)

(见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.

阅读规范,有点难以理解 block 和 inline 的含义,但是在玩弄它之后,我发现对于垂直滚动列表,block: 'end' 确保元素可见,而无需人为地滚动顶部我的内容离开视口。使用“center”,靠近底部的元素会向上滑动太多,其下方会出现空白空间。但是我的容器是一个带有 justify: 'stretch' 的 flex 父级,因此可能会影响行为。我没有进一步挖掘。隐藏溢出的元素会影响 scrollIntoView 的行为方式,因此您可能必须自己进行试验。

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.

我的应用程序有一个必须在视图中的父级,如果选择了一个子级,它也会滚动到视图中。这很有效,因为父类 DidMount 发生在子类的 DidMount 之前,所以它滚动到父类,然后在呈现活动子类时,进一步滚动以显示该子类。

回答by Steven

Another example which uses function in ref rather than string

另一个在 ref 中使用函数而不是字符串的例子

class List extends React.Component {
  constructor(props) {
    super(props);
    this.state = { items:[], index: 0 };
    this._nodes = new Map();

    this.handleAdd = this.handleAdd.bind(this);
    this.handleRemove = this.handleRemove.bind(this);
   }

  handleAdd() {
    let startNumber = 0;
    if (this.state.items.length) {
      startNumber = this.state.items[this.state.items.length - 1];
    }

    let newItems = this.state.items.splice(0);
    for (let i = startNumber; i < startNumber + 100; i++) {
      newItems.push(i);
    }

    this.setState({ items: newItems });
  }

  handleRemove() {
    this.setState({ items: this.state.items.slice(1) });
  }

  handleShow(i) {
    this.setState({index: i});
    const node = this._nodes.get(i);
    console.log(this._nodes);
    if (node) {
      ReactDOM.findDOMNode(node).scrollIntoView({block: 'end', behavior: 'smooth'});
    }
  }

  render() {
    return(
      <div>
        <ul>{this.state.items.map((item, i) => (<Item key={i} ref={(element) => this._nodes.set(i, element)}>{item}</Item>))}</ul>
        <button onClick={this.handleShow.bind(this, 0)}>0</button>
        <button onClick={this.handleShow.bind(this, 50)}>50</button>
        <button onClick={this.handleShow.bind(this, 99)}>99</button>
        <button onClick={this.handleAdd}>Add</button>
        <button onClick={this.handleRemove}>Remove</button>
        {this.state.index}
      </div>
    );
  }
}

class Item extends React.Component
{
  render() {
    return (<li ref={ element => this.listItem = element }>
      {this.props.children}
    </li>);
  }
}

Demo: https://codepen.io/anon/pen/XpqJVe

演示:https: //codepen.io/anon/pen/XpqJVe

回答by Yonatan Kogan

To build on @Michelle Tilley's answer, I sometimes want to scroll if the user's selection changes, so I trigger the scroll on componentDidUpdate. I also did some math to figure out how far to scroll and whether scrolling was needed, which for me looks like the following:

为了建立在@Michelle Tilley 的回答之上,我有时想在用户的选择发生变化时滚动,所以我在componentDidUpdate. 我还做了一些数学计算来弄清楚滚动的距离以及是否需要滚动,对我来说看起来像下面这样:

  componentDidUpdate() {
    let panel, node;
    if (this.refs.selectedSection && this.refs.selectedItem) {
      // This is the container you want to scroll.          
      panel = this.refs.listPanel;
      // This is the element you want to make visible w/i the container
      // Note: You can nest refs here if you want an item w/i the selected item          
      node = ReactDOM.findDOMNode(this.refs.selectedItem);
    }

    if (panel && node &&
      (node.offsetTop > panel.scrollTop + panel.offsetHeight || node.offsetTop < panel.scrollTop)) {
      panel.scrollTop = node.offsetTop - panel.offsetTop;
    }
  }

回答by Mike Driver

In you keyup/down handler you just need to set the scrollTopproperty of the div you want to scroll to make it scroll down (or up).

在您的 keyup/down 处理程序中,您只需要设置scrollTop要滚动的 div的属性以使其向下(或向上)滚动。

For example:

例如:

JSX:

JSX:

<div ref="foo">{content}</div>

<div ref="foo">{content}</div>

keyup/down handler:

键上/下处理程序:

this.refs.foo.getDOMNode().scrollTop += 10

this.refs.foo.getDOMNode().scrollTop += 10

If you do something similar to above, your div will scroll down 10 pixels (assuming the div is set to overflow autoor scrollin css, and your content is overflowing of course).

如果您执行与上述类似的操作,您的 div 将向下滚动 10 个像素(假设 div 设置为溢出autoscroll在 css 中,并且您的内容当然是溢出的)。

You will need to expand on this to find the offset of the element inside your scrolling div that you want to scroll the div down to, and then modify the scrollTopto scroll far enough to show the element based on it's height.

您将需要对此进行扩展,以找到滚动 div 中要向下滚动 div 的元素的偏移量,然后修改scrollTop以滚动到足够远的距离以根据元素的高度显示该元素。

Have a look at MDN's definitions of scrollTop, and offsetTop here:

在这里查看 MDN 对 scrollTop 和 offsetTop 的定义:

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop

https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop

回答by Aishwat Singh

Just in case someone stumbles here, I did it this way

以防万一有人在这里绊倒,我是这样做的

  componentDidMount(){
    const node = this.refs.trackerRef;
    node && node.scrollIntoView({block: "end", behavior: 'smooth'})
  }
  componentDidUpdate() {
    const node = this.refs.trackerRef;
    node && node.scrollIntoView({block: "end", behavior: 'smooth'})
  }

  render() {
    return (

      <div>
        {messages.map((msg, index) => {
          return (
            <Message key={index} msgObj={msg}
              {/*<p>some test text</p>*/}
            </Message>
          )
        })}

        <div style={{height: '30px'}} id='#tracker' ref="trackerRef"></div>
      </div>
    )
  }

scrollIntoViewis native DOM feature link

scrollIntoView是原生 DOM 功能链接

It will always shows trackerdiv

它总是会显示trackerdiv

回答by MartinDuo

I'm just adding another bit of info for others searching for a Scroll-To capability in React. I had tied several libraries for doing Scroll-To for my app, and none worked from my use case until I found react-scrollchor, so I thought I'd pass it on. https://github.com/bySabi/react-scrollchor

我只是为其他在 React 中搜索 Scroll-To 功能的人添加了一些信息。我绑定了几个库来为我的应用程序执行 Scroll-To,在我找到 react-scrollchor 之前,没有一个库在我的用例中起作用,所以我想我会传递它。https://github.com/bySabi/react-scrollchor

回答by Everistus Olumese

I had a NavLink that I wanted to when clicked will scroll to that element like named anchor does. I implemented it this way.

我有一个 NavLink,我想在点击它时会像命名锚一样滚动到那个元素。我是这样实现的。

 <NavLink onClick={() => this.scrollToHref('plans')}>Our Plans</NavLink>
  scrollToHref = (element) =>{
    let node;
    if(element === 'how'){
      node = ReactDom.findDOMNode(this.refs.how);
      console.log(this.refs)
    }else  if(element === 'plans'){
      node = ReactDom.findDOMNode(this.refs.plans);
    }else  if(element === 'about'){
      node = ReactDom.findDOMNode(this.refs.about);
    }

    node.scrollIntoView({block: 'start', behavior: 'smooth'});

  }

I then give the component I wanted to scroll to a ref like this

然后我给我想要滚动到这样的参考的组件

<Investments ref="plans"/>

回答by Stanislav Glushak

With reacts Hooks:

与反应挂钩:

  1. Import
  1. 进口
import ReactDOM from 'react-dom';
import React, {useRef} from 'react';
  1. Make new hook:
  1. 制作新钩子:
const divRef = useRef<HTMLDivElement>(null);
  1. Add new Div
  1. 添加新的 Div
<div ref={divRef}/>
  1. Scroll function:
  1. 滚动功能:
const scrollToDivRef  = () => {
    let node = ReactDOM.findDOMNode(divRef.current) as Element;
    node.scrollIntoView({block: 'start', behavior: 'smooth'});
}