27

I'm trying to add an onScroll event on a table. This is what I've tried:

componentDidMount() {
    ReactDOM.findDOMNode(this.refs.table).addEventListener('scroll', this.listenScrollEvent);
}

componentWillUnmount() {
    ReactDOM.findDOMNode(this.refs.table).removeEventListener('scroll', this.listenScrollEvent);
}

listenScrollEvent() {
    console.log('Scroll event detected!');
}

render() {
    return (
        <table ref="table">
           [...]
        </table>
    )
}

I tried console.log(ReactDOM.findDOMNode(this.refs.table)) and I'm getting the correct result but scroll event is never fired at all. I looked in here but still failed. Any help would be so much appreciated.

Community
  • 1
  • 1
Lord Salforis
  • 572
  • 1
  • 6
  • 16
  • 1
    Is table content overflowing table boundaries? It not, it won't scroll. – Andreyco Sep 05 '16 at 07:31
  • the container has `overflow: auto` that means the table is inside a frame(if I'm correct). I don't want to use the `window.addEventListener` – Lord Salforis Sep 05 '16 at 07:45
  • try add `display: block` to table. – Anton Kulakov Sep 05 '16 at 08:02
  • 1
    For those who are implementing the same as mine but is using django, try using [django-el-pagination](http://django-el-pagination.readthedocs.io/en/latest/twitter_pagination.html). It has `paginateOnScroll` already. – Lord Salforis Sep 06 '16 at 09:07

6 Answers6

22

You need to bind this to the element in context.

render() {
    return (
        <table ref="table" onScroll={this.listenScrollEvent.bind(this)}>
           [...]
        </table>
    )
}
Elad Katz
  • 7,483
  • 5
  • 35
  • 66
Harkirat Saluja
  • 7,768
  • 5
  • 47
  • 73
  • 3
    If you use ES6 Class for creating component. – Anton Kulakov Sep 05 '16 at 08:01
  • I already did at the constructor but still doesn't work. `constructor(props) {super(props); this.listenScrollEvent = this.listenScrollEvent.bind(this);}` – Lord Salforis Sep 05 '16 at 08:03
  • 1
    While this is generally useful, note that the `listenScrollEvent` method in the original question doesn't do anything with `this` so binding it is actually unnecessary. – Jules Jul 21 '17 at 23:37
  • Well this actually helps me for some reason. It is simple and to the point – Faris Rayhan Mar 28 '18 at 07:09
6

You can use onScroll attribute:

listenScrollEvent() {
    console.log('Scroll event detected!');
}

render() {
    return (
        <table onScroll={this.listenScrollEvent}>
           [...]
        </table>
    )
}

Here is an example: https://jsfiddle.net/81Lujabv/

Anton Kulakov
  • 482
  • 1
  • 3
  • 13
6

I was looking to do something similar. Adding the event listener to the window instead of the ReactDom.findDOMNode worked for me...

componentDidMount() {
    window.addEventListener('scroll', this.handleScrollToElement);
}

componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScrollToElement);
}

handleScrollToElement(event) {
    console.log('Fired ' + event)
}
tlagreca
  • 358
  • 1
  • 6
  • 20
  • I suspect this is the correct answer even though the asker said he didn't want to use `window...` in the comments. Yet, in the comments he also failed to answer the first question of whether the table contents are overflowing. I suspect the answer is really "no" since no other answers work for him. He doesn't tell us much about his ultimate goal so I can only guess he's going for a fixed/absolute table that will never scroll except with `window`. I was having similar trouble at first until I got the hint that scroll only fires on objects that overflow, so I looked it up and, yup, that was it. – juanitogan Sep 18 '18 at 00:41
  • you can have more than 1 scrolling component on a window, so not so great. – Jason G Feb 17 '20 at 20:02
1

according to React documents(https://reactjs.org/docs/handling-events.html),

React events are named using camelCase, rather than lowercase. You can set attributes as you do with pure HTML.

HTML:

<div onclick="..." onscroll="...">
  ...
</div>

JSX:

<div onClick={...} onScroll={...}>
  ...
</div>

you should create a wrapper block element which has fixed height to enable scroll.

kidkkr
  • 457
  • 3
  • 16
0

I had been finding a good solution to this problem. The following piece of code is working, where the listener is just on the particular class/div : React version is 16.0.0

First import ReactDOM, import ReactDOM from "react-dom";

Then in the class xyz extends Component section

  constructor(props) {
    super();
    this.state = {
        vPos : 0
    }
    this.listenScrollEvent = this.listenScrollEvent.bind(this);
  }

  componentDidMount() {
    ReactDOM.findDOMNode(this.refs.category_scroll).addEventListener('scroll', this.listenScrollEvent);
  }

  componentWillUnmount() {
      ReactDOM.findDOMNode(this.refs.category_scroll).removeEventListener('scroll', this.listenScrollEvent);
  }

  listenScrollEvent(event) {
    console.log('firee');
    this.setState({
        vPos: event.target.body.scrollTop
    });
  }

After pasting the above functions, in the render() method , whatever you want to name the ref method, you can do so, make sure the same name goes in the findDOMNode as well , i.e, findDOMNode(this.refs.category_scroll):

<div onScroll={this.listenScrollEvent} ref="category_scroll">
...
</div>

.

.

If it's a horizontall scroll, then event.currentTarget.scrollTop works in the listenScrollEvent() function.

Shivansh Jagga
  • 1,541
  • 1
  • 15
  • 24
-1

Give this a try, added .bind(this) to this.listenScrollEvent when you pass it to the addEventListener.

   componentDidMount() {
        ReactDOM.findDOMNode(this.refs.table).addEventListener('scroll', this.listenScrollEvent.bind(this));
    }

    componentWillUnmount() {
        ReactDOM.findDOMNode(this.refs.table).removeEventListener('scroll', this.listenScrollEvent.bind(this));
    }

    listenScrollEvent() {
        console.log('Scroll event detected!');
    }

    render() {
        return (
            <table ref="table">
               [...]
            </table>
        )
    }
MCSLI
  • 321
  • 4
  • 6