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

时间:2015-05-27 23:57:12

标签: javascript html css scroll reactjs

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

现在,如果我按下向下键足够多次,则突出显示的项目将不再可见。如果滚动视图,则向上键也会出现同样的情况。

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

8 个答案:

答案 0 :(得分:76)

我假设你有某种List组件和某种Item组件。我这样做in one project的方法是让项目知道它是否有效;如果需要,该项目会要求列表将其滚动到视图中。考虑以下伪代码:

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));
    }
  }
}

更好的解决方案可能是让列表负责将项目滚动到视图中(项目不知道它甚至在列表中)。为此,您可以为特定项目添加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.
  }
}

如果您不想进行数学计算以确定该项是否在列表节点中可见,则可以使用DOM method scrollIntoView()或特定于Webkit的scrollIntoViewIfNeededmyfunction\((.*?)\)(?=(([^'"]*['"]){2})*[^'"]*$) 3}}所以你可以在非Webkit浏览器中使用它。

答案 1 :(得分:6)

另一个使用ref而不是string

中的函数的例子
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>);
  }
}

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

答案 2 :(得分:4)

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

class Something extends Component {
  constructor(props) {
    super(props);
    this.boxRef = React.createRef();
  }

  render() {
    return (
      <div ref={this.boxRef} />
    );
  }
}

然后滚动,只需添加(在构造函数之后):

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

注意:您必须使用“ .current”,并且可以发送选项来scrollIntoView:

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

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

阅读规范后,很难理解block和inline的含义,但是在玩了之后,我发现对于垂直滚动列表,block:'end'确保该元素可见而无需人为地将我的内容顶部滚动到视口之外。如果使用“中心”,则靠近底部的元素会向上滑动太远,并且在其下方出现空白区域。但是我的容器是一个有理由的flex父级:“拉伸”,因此可能会影响行为。我没有进一步挖掘。隐藏了溢出的元素将影响scrollIntoView的行为,因此您可能必须自己进行实验。

我的应用程序有一个必须在视图中的父级,如果选择了一个子级,那么它也将滚动到视图中。效果很好,因为父项DidMount发生在子项的DidMount之前,因此它滚动到父项,然后在渲染活动子项时,进一步滚动以使该子项可见。

答案 3 :(得分:1)

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

例如:

JSX:

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

keyup / down handler:

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

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

您需要对此进行扩展,以找到要将div向下滚动到滚动div内的元素的偏移量,然后修改scrollTop以滚动到足以显示基于此元素的元素它的高度。

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

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

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

答案 4 :(得分:1)

万一有人偶然发现,我这样做了

  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>
    )
  }

scrollIntoView是原生DOM功能link

它将始终显示tracker div

答案 5 :(得分:0)

我只是为其他人在React中搜索Scroll-To功能添加了一些信息。我为我的应用程序绑了几个库来做Scroll-To,直到我找到react-scrollchor才能使用我的用例,所以我想我会把它传递给我。 https://github.com/bySabi/react-scrollchor

答案 6 :(得分:0)

我有一个想要链接的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'});

  }

然后我将要滚动的组件提供给这样的引用

<Investments ref="plans"/>

答案 7 :(得分:0)

与反应挂钩:

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