如何滚动一个div,使之在堆js中可见?

时间:2023-01-22 21:30:22

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.

我有一个弹出列表,它是一个div,包含一个垂直的子div列表。我已经添加了向上/向下的键盘导航以更改当前突出显示的子元素。

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.

现在,如果按下向下键的次数足够多,高亮显示的项将不再可见。如果视图被滚动,那么up键也会发生同样的事情。

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

什么是正确的方法,以自动滚动子div进入视图?

6 个解决方案

#1


65  

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:

更好的解决方案可能是让列表负责将项目滚动到视图中(不让项目知道它甚至在列表中)。为此,您可以向某个项目添加一个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 available so you can use it in non-Webkit browsers.

如果您不想进行计算,以确定列表节点中是否可见该项,您可以使用DOM方法scrollIntoView()或webkit特有的scrollintoviewifneed,它有一个可用的polyfill,因此您可以在非webkit浏览器中使用它。

#2


5  

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

#3


5  

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

#4


1  

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

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

For example:

例如:

JSX:

JSX:

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

< div ref = " foo " > {内容} < / div >

keyup/down handler:

keyup /处理程序:

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 auto or scroll in css, and your content is overflowing of course).

如果您做了类似的事情,您的div将向下滚动10个像素(假设div被设置为自动溢出或在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 scrollTop to scroll far enough to show the element based on it's height.

您需要在此基础上展开,以找到要向下滚动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

#5


0  

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中为其他搜索scrollto功能的人添加了一点信息。我已经为我的应用程序绑定了几个库,并且没有从我的用例中工作,直到我找到了反应-scrollchor,所以我想把它传递下去。https://github.com/bySabi/react-scrollchor

#6


0  

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

scrollIntoView is native DOM feature link

scrollIntoView是本地DOM特性链接

It will always shows tracker div

它将始终显示跟踪器div

#1


65  

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:

更好的解决方案可能是让列表负责将项目滚动到视图中(不让项目知道它甚至在列表中)。为此,您可以向某个项目添加一个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 available so you can use it in non-Webkit browsers.

如果您不想进行计算,以确定列表节点中是否可见该项,您可以使用DOM方法scrollIntoView()或webkit特有的scrollintoviewifneed,它有一个可用的polyfill,因此您可以在非webkit浏览器中使用它。

#2


5  

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

#3


5  

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

#4


1  

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

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

For example:

例如:

JSX:

JSX:

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

< div ref = " foo " > {内容} < / div >

keyup/down handler:

keyup /处理程序:

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 auto or scroll in css, and your content is overflowing of course).

如果您做了类似的事情,您的div将向下滚动10个像素(假设div被设置为自动溢出或在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 scrollTop to scroll far enough to show the element based on it's height.

您需要在此基础上展开,以找到要向下滚动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

#5


0  

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中为其他搜索scrollto功能的人添加了一点信息。我已经为我的应用程序绑定了几个库,并且没有从我的用例中工作,直到我找到了反应-scrollchor,所以我想把它传递下去。https://github.com/bySabi/react-scrollchor

#6


0  

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

scrollIntoView is native DOM feature link

scrollIntoView是本地DOM特性链接

It will always shows tracker div

它将始终显示跟踪器div