2015-09-19 10 views
18

Reactでリストビューを実装しようとしています。 私が達成しようとしているのは、リストヘッダ情報を格納し、コンポーネントを登録してスクロールイベントを登録することです。 ユーザーがウィンドウをスクロールするたびに、保存されたdivを取り出して、offsetTopデータを再計算したいと思います。リアクションでdivのoffsetTopポジションを取得

問題は、コンソールがちょうど最初の値をプリントアウトしていることです(値は固定されており、決して変更されません)データはonscrollの機能で決して変更されません。

誰でも_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

+0

「実際の」オフセットを取得するには、おそらくoffsetTopに '' 'scrollTop'''値を追加する必要があります。私はgithub上であなたのコードを得ることができませんでしたので、私はそれを試すことができませんでした:/ –

+0

@PatrickNeschkudlaは本当ですか?どのようなエラーがありますか?おそらくnpmにwebpackを最初にインストールする必要があります。 – JavaScripter

+0

Reactは 'React.findDOMNode'を独自のパッケージ' react-dom'に移動したことを覚えておいてください。 [here](https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode) – aug

答えて

36

あなたは、トップがあなたの要素のオフセットを取得するElement.getBoundingClientRect()メソッドを使用することを奨励することができます。このメソッドは、ビューポート内の要素の完全なオフセット値(左、上、右、下、幅、高さ)を提供します。

John Resig's postに、この方法の有用性を説明しています。

+1

を参照してください。この方法では、オフセット。 – vise

+0

@viseあなたはoffsetTopかgetBoundingClientRect以外の何かを使って、この質問に対してより良い答えを提供できますか? – TheJKFever

+0

外部URLにリンクする際に、今後URLが無効になる場合のためにコンテンツまたは要約を追加してください。 –

8

ユージンの答えは、データを取得するために、正しい機能を使用していますが、後世のために、私は(this answerによる)+ v0.14を反応させるのにそれを使用する方法を正確に綴るしたいと思います:

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

です完全に私のために働いて、私はちょうどマウントされた新しいコンポーネントの上にスクロールするためにデータを使用しています。

+0

この回答は2018年2月2日私のために働いた。 – agm1984

3

findDOMNodeが避けられるようにrefを使用した方がよい解決策です。

... 
onScroll() { 
    let offsetTop = this.instance.getBoundingClientRect().top; 
} 
... 
render() { 
... 
<Component ref={(el) => this.instance = el } /> 
... 
関連する問題