I'm trying to setup a Marquee in React if a piece of text is greater than its container but I can't get the correct width of the container, even after the component has rendered.
I read in another answer React “after render” code? that you have to use requestAnimationFrame which I'm trying and it's still not working.
If I log the width of the container it shows a width of 147px which is set using min-width in the stylesheet but the correct width should be 320px which is set using a media query when the screens min-width is 600px.
This is a child component, the parent is rendered inside an iFrame if it makes any difference and the iFrame's width is well over 600px.
The JS:
module.exports = React.createClass({
componentDidUpdate: function () {
// Setup marquee
this.initMarquee();
},
render: function () {
// Setup marquee
this.initMarquee();
var artistName = this.props.artist.artistName;
var trackName = this.props.track.trackName;
return (
<div className="MV-player-trackData no-select" ref="mvpTrackData">
<div className="MV-player-trackData-marquee-wrap" ref="mvpMarqueeWrap">
<div className="MV-player-trackData-marquee" ref="mvpMarquee">
<a className="MV-player-trackData-link no-select" href={this.props.storeUrl} target="_blank">
<span id="mvArtistName">{artistName}</span> – <span id="mvTrackName">{trackName}</span>
</a>
</div>
</div>
</div>
)
},
initMarquee: function () {
if ( typeof requestAnimationFrame !== 'undefined' ) {
//store a this ref, and
var self = this;
//wait for a paint to setup marquee
window.requestAnimationFrame(function() {
self.marquee();
});
}
else {
// Suport older browsers
window.setTimeout(this.marquee, 2000);
}
},
marquee: function () {
var marquee = React.findDOMNode(this.refs.mvpMarquee);
var marqueeWrap = React.findDOMNode(this.refs.mvpMarqueeWrap);
// If the marquee is greater than its container then animate it
if ( marquee.clientWidth > marqueeWrap.clientWidth ) {
marquee.className += ' is-animated';
}
else {
marquee.className = marquee.className.replace('is-animated', '');
}
}
});
The CSS:
.MV-player-trackData-marquee-wrap {
height: 20px;
line-height: 20px;
min-width: 147px;
overflow: hidden;
position: relative;
width: 100%;
@media only screen and (min-width : 600px) {
min-width: 320px;
}
}
I've tried a number of different solutions including laidout and react-component-width-mixin but neither of them work. I tried react component width mixin because in another part of my app I'm trying to get the value of window.innerWidth
but that also returns 0 after rendering, unless I set a timeout for around 2 seconds, unfortunately though sometimes 2 seconds isn't long enough due to data loading and other callbacks so this can brake easily.
Any help is really appreciated. Thanks.
Update:
One of the answers correctly pointed out i should be calling this.initMarquee();
inside componentDidMount
which I was doing, unfortunately I pasted the wrong code from when I was testing to see if it made a difference calling it inside render
. The correct code looks like this:
componentDidMount: function () {
// Setup marquee
this.initMarquee();
},
Unfortunately this doesn't work either, I still receive the incorrect width for marqueeWrap.
Update: 24/06/2015 Just to clarify, this is the marquee effect I'm trying to achieve, only when the text is bigger than its container as it's pointless scrolling it when it is not bigger.
Also here is a link to a Github Issue from the React team speaking about why React renders before the browser paints. - So as that is the case, I want to know how do I reliably get the width of the element in question.