在 React 中获取 div 的 offsetTop 位置

Get div's offsetTop positions in React

我正在尝试在 React 中实现列表视图。 我想要实现的是存储列表 headers 信息并注册组件和注册滚动事件。 每次当用户滚动 window 时,我想取出存储的 div 和 re-calculate offsetTop 数据。

现在的问题是,我发现控制台只是打印出初始值(该值是固定的,永远不会改变)offsetTop数据在onscroll函数中永远不会改变。

有人建议如何从 _instances object 获取最新的 offsetTop 吗?

import React, { Component } from 'react';
import ListHeader from './lib/ListHeader';
import ListItems from './lib/ListItems';

const styles = {
  'height': '400px',
  'overflowY': 'auto',
  'outline': '1px dashed red',
  'width': '40%'
};

class HeaderPosInfo {
  constructor(headerObj, originalPosition, originalHeight) {
    this.headerObj = headerObj;
    this.originalPosition = originalPosition;
    this.originalHeight = originalHeight; 
  }
}

export default class ReactListView extends Component {
  static defaultProps = {
    events: ['scroll', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll', 'resize', 'touchmove', 'touchend'],
    _instances:[],
    _positionMap: new Set(),
    _topPos:'',
    _topWrapper:''
  }

  static propTypes = {
    data: React.PropTypes.array.isRequired,
    headerAttName: React.PropTypes.string.isRequired,
    itemsAttName: React.PropTypes.string.isRequired,
    events: React.PropTypes.array,
    _instances: React.PropTypes.array,
    _positionMap: React.PropTypes.object,
    _topPos: React.PropTypes.string,
    _topWrapper: React.PropTypes.object
  };

  state = {
    events: this.props.events,
    _instances: this.props._instances,
    _positionMap: this.props._positionMap,
    _topPos: this.props._topPos
  }

  componentDidMount() {
    this.initStickyHeaders();
  }

  componentWillUnmount() {

  }

  componentDidUpdate() {

  }

  refsToArray(ctx, prefix){
    let results = [];
    for (let i=0;;i++){
      let ref = ctx.refs[prefix + '-' + String(i)];
      if (ref) results.push(ref);
      else return results;
    }
  }

  initHeaderPositions() {
    // Retrieve all instance of headers and store position info
    this.props._instances.forEach((k)=>{
      this.props._positionMap.add(new HeaderPosInfo(
          k, 
          k.refs.header.getDOMNode().offsetTop,
          k.refs.header.getDOMNode().offsetHeight
        ));
    });
    let it = this.props._positionMap.values();
    let first = it.next();
    this.props._topPos = first.value.originalPosition;
    this.props._topWrapper = first.value.headerObj;
  }

  initStickyHeaders () {
    this.props._instances = this.refsToArray(this, 'ListHeader');
    this.initHeaderPositions();

    // Register events listeners with the listview div
    this.props.events.forEach(type => {
      if (window.addEventListener) {
        React.findDOMNode(this.refs.listview).addEventListener(type, this.onScroll.bind(this), false);
      } else {
        React.findDOMNode(this.refs.listview).attachEvent('on' + type, this.onScroll.bind(this), false);
      }
    });
  }

  onScroll() {

    // update current header positions and apply fixed positions to the top one
    console.log(1);
    let offsetTop  = React.findDOMNode(this.props._instances[0].refs.header).offsetTop;

  }

  render() {
    const { data, headerAttName, itemsAttName } = this.props;
    let _refi = 0;
    let makeRef = () => {
      return 'ListHeader-' + (_refi++);
    };

    return (
      <div ref="listview" style={styles}>
      {
        Object.keys(data).map(k => {
        const header = data[k][headerAttName];
        const items  = data[k][itemsAttName];
          return (
            <ul key={k}>     
              <ListHeader ref={makeRef()} header={header} />
              <ListItems  items={items} />
            </ul>
          );
        })
      }
      </div>
    );
  }
}

完整的源代码在Github,你可以从这里克隆和编译它:

Github

我们建议您使用 Element.getBoundingClientRect() 方法来获取元素的顶部偏移量。此方法提供元素在视口中的完整偏移值(左、上、右、下、宽、高)。

检查 John Resig's post 描述此方法的帮助程度。

Eugene 的回答使用了正确的函数来获取数据,但为了后代,我想详细说明如何在 React v0.14+ 中使用它(根据 ):

  import ReactDOM from 'react-dom';
  //...
  componentDidMount() {
    var rect = ReactDOM.findDOMNode(this)
      .getBoundingClientRect()
  }

非常适合我,我正在使用数据滚动到刚刚安装的新组件的顶部。

使用 ref 的更好解决方案,以避免不鼓励的 findDOMNode。

...
onScroll() {
    let offsetTop  = this.instance.getBoundingClientRect().top;
}
...
render() {
...
<Component ref={(el) => this.instance = el } />
...
  import ReactDOM from 'react-dom';
  //...
  componentDidMount() {
    var n = ReactDOM.findDOMNode(this);
    console.log(n.offsetTop);
  }

您可以直接从节点中获取 offsetTop。

如果您使用的是 React 16.3 及更高版本,则更快捷的方法是在构造函数中创建一个 ref,然后将其附加到您希望使用的组件,如下所示。

...
constructor(props){
   ...
   //create a ref
   this.someRefName = React.createRef();

}

onScroll(){
let offsetTop = this.someRefName.current.offsetTop;

}

render(){
...
<Component ref={this.someRefName} />

}

我确实意识到作者提出了与基于 class 的组件相关的问题,但是我认为值得一提的是,从 React 16.8.0 (February 6, 2019) 开始,您可以利用基于函数的钩子组件。

示例代码:

import { useRef } from 'react'

function Component() {
  const inputRef = useRef()

  return (
    <input ref={inputRef} />
    <div
      onScroll={() => {
        const { offsetTop } = inputRef.current
        ...
      }}
    >
  )
}

onScroll 有一个事件,其中包含此 div 中的所有本机元素和子元素,因此您可以如下所示使用它并获取目标元素 offsetTop。

const getoffSet = e => {
   console.log(e, e.natiiveEvent.target.childNodes[0].offsetTop)
}

return (
   <div onScroll={(e) => getoffSet(e)} ref={listview} style={styles}> 
   </div>
)

检查高度 属性 是否未设置为 Parent:

  • 如果 parent 元素没有设置高度,那么粘性元素不会 滚动时有任何区域可以坚持。发生这种情况是因为
    粘性元素意味着 stick/scroll 在 a
    的高度内 容器.

检查 Parent 元素是否为 Flexbox

  • 如果sticky元素的parent是flexbox,有两种情况 检查:

    粘性元素有 align-self: 自动设置(这是默认设置); 粘性元素具有 align-self: 拉伸集。如果粘性元素 有 align-self: auto Set: 在这种情况下,align-self 的值将 计算 parent 的 align-items 值。所以,

如果 parent 具有 align-items: 正常(默认)或 align-items: stretch set,那么这意味着粘性元素的高度将拉伸以填充整个可用 space。这将使粘性元素没有空间在 parent.

中滚动

如果粘性元素有align-self: stretch Set:

在这种情况下,粘性元素会拉伸到 parent 的高度,并且没有任何可滚动的区域。

如何使粘性元素在 Flexbox 中可滚动: 您可以简单地将 align-self 属性 的值设置为 align-self: flex-start。这会将粘性元素放在开头并且不会拉伸它。enter link description here