2015-09-19 12 views
18

Tôi đang cố gắng triển khai chế độ xem Danh sách trong React. Điều tôi đang cố gắng đạt được là lưu trữ thông tin tiêu đề danh sách và đăng ký các thành phần và đăng ký sự kiện cuộn. mỗi khi người dùng cuộn cửa sổ, tôi muốn lấy div đã lưu và tính lại dữ liệu offsetTop.Nhận vị trí offsetTop của div trong React

Vấn đề bây giờ là, tôi thấy bảng điều khiển chỉ in ra giá trị ban đầu (giá trị được cố định và không bao giờ thay đổi) offsetTop dữ liệu không bao giờ thay đổi trong hàm onscroll.

Bất kỳ ai đề xuất cách nhận mới nhất offsetTop từ đối tượng _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> 
    ); 
    } 
} 

Mã nguồn toàn là trên Github, bạn có thể sao chép và biên dịch nó từ đây:

Github

+0

Bạn có thể cần phải thêm giá trị '' 'scrollTop''' vào offsetTop để nhận được bù đắp" thực ". Tôi không thể nhận được mã của bạn trên github để làm việc, vì vậy tôi không thể thử nó mặc dù:/ –

+0

@ PatrickNeschkudla thực sự? bạn có lỗi gì có lẽ bạn cần npm cài đặt webpack đầu tiên. – JavaScripter

+0

Chỉ là một FYI cho tất cả mọi người mà tình cờ về điều này, hãy nhớ rằng React đã di chuyển 'React.findDOMNode' vào gói riêng của nó' phản ứng-dom'. Xem [tại đây] (https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode) – aug

Trả lời

36

Bạn có thể được khuyến khích sử dụng các phương pháp Element.getBoundingClientRect() để đầu bù đắp các yếu tố của bạn . Phương thức này cung cấp các giá trị bù đắp đầy đủ (trái, trên, phải, dưới, chiều rộng, chiều cao) của phần tử trong khung nhìn.

Kiểm tra John Resig's post mô tả mức độ hữu ích của phương pháp này.

+1

Tôi muốn chỉ ra rằng phương pháp này phải chịu một hình phạt hiệu suất đáng kể so với việc chỉ nhận bù đắp. – vise

+0

@vise bạn có thể cung cấp câu trả lời tốt hơn cho câu hỏi này bằng cách sử dụng chỉ offsetTop hoặc một cái gì đó khác mà getBoundingClientRect không? – TheJKFever

+0

Vui lòng khi liên kết với các url bên ngoài thêm nội dung hoặc bản tóm tắt trong trường hợp url trở nên không hợp lệ trong tương lai. –

8

câu trả lời của Eugene sử dụng hàm đúng để có được các dữ liệu, nhưng đối với hậu thế, tôi muốn giải thích rõ ràng chính xác làm thế nào để sử dụng nó trong Phản ứng v0.14 + (theo this answer):

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

Is làm việc cho tôi một cách hoàn hảo và tôi đang sử dụng dữ liệu để cuộn lên đầu thành phần mới vừa được gắn kết.

+0

Câu trả lời này làm việc cho tôi ngày 2 tháng 2 năm 2018. – agm1984

3

Một giải pháp tốt hơn với ref để tránh findDOMNode không được khuyến khích.

... 
onScroll() { 
    let offsetTop = this.instance.getBoundingClientRect().top; 
} 
... 
render() { 
... 
<Component ref={(el) => this.instance = el } /> 
... 
Các vấn đề liên quan