2015-09-19 4 views
18

Ich versuche, eine List-Ansicht in React zu implementieren. Was ich versuche zu erreichen, ist, dass die Liste Kopfzeilen Informationen zu speichern und registrieren Sie die Komponenten und registrieren Sie das Scroll-Ereignis. Jedes Mal, wenn der Benutzer das Fenster scrollt, möchte ich das gespeicherte div entfernen und die offsetTop Daten neu berechnen.Erhalten divs OffsetTop-Positionen in React

Das Problem ist jetzt, dass ich fand die Konsole nur den Anfangswert ausdrucken (der Wert ist fixiert und nie geändert) offsetTop Daten ändern sich nie in onscroll Funktion.

Wer schlägt vor, wie man offsetTop aus dem _instances Objekt erhalten?

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

Der gesamte Quellcode ist auf Github, können Sie es klonen und kompilieren von hier:

Github

+0

Wahrscheinlich müssen Sie dem OffsetTop den '' 'scrollTop''' Wert hinzufügen, um den" echten "Offset zu erhalten. Ich konnte deinen Code auf Github nicht zur Arbeit bringen, also konnte ich es nicht versuchen:/ –

+0

@PatrickNeschkudla wirklich? Welche Fehler hast du? Wahrscheinlich brauchst du npm zuerst das Webpack installieren. – JavaScripter

+0

Nur ein Hinweis für alle, die darauf stolpern, denken Sie daran, dass React "React.findDOMNode" in sein eigenes Paket "react-dom" verschoben hat. Siehe [hier] (https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode) – aug

Antwort

36

Sie ermutigt werden, kann die Element.getBoundingClientRect() Methode zu verwenden, die Top-Offset des Elements erhalten . Diese Methode bietet die vollständigen Versatzwerte (links, oben, rechts, unten, Breite, Höhe) Ihres Elements im Ansichtsfenster.

Überprüfen Sie die John Resig's post beschreiben, wie hilfreich diese Methode ist.

+1

Ich möchte darauf hinweisen, dass diese Methode eine erhebliche Leistungseinbuße im Vergleich zu nur erhalten verursacht der Offset. – vise

+0

@vise können Sie eine bessere Antwort auf diese Frage mit nur OffsetTop oder etwas anderes, dass GetBoundingClientRect? – TheJKFever

+0

Bitte fügen Sie beim Link zu externen URLs den Inhalt oder eine Zusammenfassung hinzu, falls die URL in Zukunft ungültig wird. –

8

Eugens Antwort verwendet die korrekte Funktion, die Daten zu bekommen, aber für die Nachwelt würde Ich mag genau darzulegen, wie es in V0.14 + Reagieren zu verwenden (nach this answer):

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

Ist arbeitet perfekt für mich, und ich benutze die Daten, um zum Anfang der neuen Komponente zu scrollen, die gerade gemountet wurde.

+0

Diese Antwort funktionierte für mich 2. Februar 2018. – agm1984

3

Eine bessere Lösung mit ref zu vermeiden findDOMNode, die nicht empfohlen wird.