Min*_*ice 51 html javascript css scroll reactjs
我有一个弹出列表,其中div包含一个子列表的垂直列表div.我添加了向上/向下键盘导航以更改当前突出显示的子项.
现在,如果我按下向下键足够多次,突出显示的项目将不再可见.如果滚动视图,则向上键也会出现同样的情况.
React自动将子项滚动div到视图中的正确方法是什么?
Mic*_*ley 78
我假设你有某种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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
一个更好的解决方案可能是让列表负责将项目滚动到视图中(没有项目知道它甚至在列表中).为此,您可以将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.
}
}
Run Code Online (Sandbox Code Playgroud)
如果您不想进行数学计算以确定该项是否在列表节点中可见,则可以使用DOM方法scrollIntoView()或特定于Webkit scrollIntoViewIfNeeded,它具有可用的polyfill,因此您可以在非Webkit浏览器中使用它.
为了建立@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;
}
}Run Code Online (Sandbox Code Playgroud)
另一个在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>);
}
}
Run Code Online (Sandbox Code Playgroud)
演示:https://codepen.io/anon/pen/XpqJVe
对于React 16,正确答案与之前的答案不同:
class Something extends Component {
constructor(props) {
super(props);
this.boxRef = React.createRef();
}
render() {
return (
<div ref={this.boxRef} />
);
}
}
Run Code Online (Sandbox Code Playgroud)
然后滚动,只需添加(在构造函数之后):
componentDidMount() {
if (this.props.active) { // whatever your test might be
this.boxRef.current.scrollIntoView();
}
}
Run Code Online (Sandbox Code Playgroud)
注意:您必须使用“ .current”,并且可以向scrollIntoView发送选项:
scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center',
});
Run Code Online (Sandbox Code Playgroud)
(位于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之前,因此它滚动到父项,然后在渲染活动子项时,进一步滚动以使该子项可见。
| 归档时间: |
|
| 查看次数: |
73906 次 |
| 最近记录: |