2015-09-19 11 views
18

Sto tentando di implementare una visualizzazione elenco in React. Quello che sto cercando di ottenere è quello di memorizzare le informazioni dell'intestazione dell'elenco e registrare i componenti e registrare l'evento di scorrimento. ogni volta che l'utente scorre la finestra, vorrei prelevare il div memorizzato e ricalcolare i dati offsetTop.Ottieni offset div Più posizioni in React

Il problema ora è che ho trovato la console che stampa il valore iniziale (il valore è fisso e mai modificato) i dati offsetTop non cambiano mai nella funzione onscroll.

Qualcuno suggerisce come ottenere l'ultimo offsetTop dall'oggetto _instances?

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

Il codice sorgente insieme è su Github, è possibile clonare e compilarlo da qui:

Github

+0

Probabilmente è necessario aggiungere il valore '' 'scrollTop''' all'offsetTop per ottenere l'offset" reale ". Non riuscivo a far funzionare il tuo codice su github, quindi non potevo provarlo però:/ –

+0

@PatrickNeschkudla davvero? che errori hai? probabilmente hai bisogno di installare npm webpack prima. – JavaScripter

+0

Solo una FYI per tutti quelli che inciampano su questo, ricorda che React ha spostato 'React.findDOMNode' nel proprio pacchetto' react-dom'. Vedi [here] (https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode) – aug

risposta

36

Si può essere incoraggiati a utilizzare il metodo Element.getBoundingClientRect() per ottenere la parte superiore di offset del vostro elemento . Questo metodo fornisce i valori di offset completi (a sinistra, in alto, a destra, in basso, in larghezza, in altezza) dell'elemento nella finestra.

Controllare il John Resig's post descrivendo l'utilità di questo metodo.

+1

Vorrei sottolineare che questo metodo incorre in una significativa penalizzazione delle prestazioni rispetto all'ottenimento l'offset. – vise

+0

@vise puoi fornire una risposta migliore a questa domanda usando solo offsetTop o qualcosa di diverso da getBoundingClientRect? – TheJKFever

+0

Per favore quando si collega a url esterni aggiungere il contenuto o un sommario nel caso in cui l'url non sia più valido in futuro. –

8

la risposta di Eugenio utilizza la funzione corretta per ottenere i dati, ma per i posteri vorrei precisare esattamente come usarlo in React v0.14 + (secondo this answer):

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

E` funziona perfettamente per me e sto utilizzando i dati per scorrere fino alla cima del nuovo componente appena montato.

+0

Questa risposta ha funzionato per me 2 febbraio 2018. – agm1984

3

Una soluzione migliore con ref per evitare findDOMNode scoraggiato.

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