【问题标题】:Get div's offsetTop positions in React在 React 中获取 div 的 offsetTop 位置
【发布时间】:2015-12-16 12:53:48
【问题描述】:

我正在尝试在 React 中实现一个列表视图。 我想要实现的是存储列表标题信息并注册组件并注册滚动事件。 每次用户滚动窗口时,我想取出存储的div并重新计算offsetTop数据。

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

有人建议如何从_instances 对象中获取最新的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

【问题讨论】:

  • 您可能需要将 scrollTop 值添加到 offsetTop 以获得“真实”偏移量。我无法让您在 github 上的代码正常工作,所以我无法尝试:/
  • @PatrickNeschkudla 真的吗?你有什么错误?可能你首先需要 npm install webpack。
  • 仅供所有偶然发现此问题的人参考,请记住 React 已将 React.findDOMNode 移动到自己的包 react-dom 中。见here

标签: javascript scroll reactjs offset


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我确实意识到作者提出了与基于类的组件有关的问题,但我认为值得一提的是,从 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
            ...
          }}
        >
      )
    }
    

    【讨论】:

    • 这似乎只给出了距离最近的父节点的 offsetTop -- 是否有一个距视口的偏移量?
    • 这是最好的答案!你帮了我很多!常量 { offsetTop, offsetLeft, offsetHeight } = inputRef.current; console.log('offsettop', offsetTop); console.log('offsetLeft', offsetLeft); console.log('offsetHeight', offsetHeight);
    【解决方案3】:

    如果您使用 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} />
    

    }

    【讨论】:

    • 这不仅更快,而且是为任何版本的 react >= 16.x 创建 ref 的正确方法
    【解决方案4】:
      import ReactDOM from 'react-dom';
      //...
      componentDidMount() {
        var n = ReactDOM.findDOMNode(this);
        console.log(n.offsetTop);
      }
    

    您可以从节点中获取 offsetTop。

    【讨论】:

      【解决方案5】:

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

      ...
      onScroll() {
          let offsetTop  = this.instance.getBoundingClientRect().top;
      }
      ...
      render() {
      ...
      <Component ref={(el) => this.instance = el } />
      ...
      

      【讨论】:

      • onscroll函数无法访问this.instance?或者看起来功能之外的任何东西
      • 需要在组件构造函数中绑定onScroll
      【解决方案6】:

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

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

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

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

        • 我想指出,与仅获取偏移量相比,此方法会导致显着的性能损失。
        • @vise 您能否仅使用 offsetTop 或 getBoundingClientRect 以外的其他方法为这个问题提供更好的答案?
        • 请在链接到外部网址时添加内容或摘要,以防网址将来失效。
        • 这个方法势在必行,功能狂人都在磨牙。它们不会为您提供更好的性能解决方案。
        • 澄清一下。您必须使用 DOM 元素 div、输入等...它不适用于您的 React 组件(例如您自己的输入、剑道等)。
        猜你喜欢
        • 1970-01-01
        • 2020-12-28
        • 2019-04-06
        • 2020-05-01
        • 1970-01-01
        • 2010-11-05
        • 2020-02-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多