remeasure
Get position and size of the DOM element for any React Component
Table of contents
Usage
// ES2015
import { measure, Measured } from "remeasure";
// CommonJS
const { measure, Measured } = require("remeasure").default;
// old school script
var measure = window.Remeasure.measure;
var Measured = window.Remeasure.Measured;
As a decorator
@measure
class MyComponent extends React.Component {
render() {
const { height, width } = this.props;
return <div>I have access to my height and width through props!</div>;
}
}
const StatelessComponent = measure(({ height, width }) => {
return <div>In here too!</div>;
});
As a component
You can use the component with function-rendering components one of three ways:
// using a children method
const MyMeasuredComponent = () => {
return (
<Measured height width>
{({ height, width }) => {
return (
<div>
My height is {height} and my width is {width}
</div>
);
}}
</Measured>
);
};
// using a component method
const MyMeasuredComponent = () => {
return (
<Measured
height
width
component={({ height, width }) => {
return (
<div>
My height is {height} and my width is {width}
</div>
);
}}
/>
);
};
// using a render method
const MyMeasuredComponent = () => {
return (
<Measured
height
width
render={({ height, width }) => {
return (
<div>
My height is {height} and my width is {width}
</div>
);
}}
/>
);
};
For performance reasons, it is recommended that you store the component as a separate method rather than create a method inline:
// using a render method
const Render = ({ height, width }) => {
return (
<div>
My height is {height} and my width is {width}
</div>
);
};
const MyMeasuredComponent = () => {
return <Measured height width render={Render} />;
};
Measurements
The following properties are available for measurement:
{
bottom: Number,
clientLeft: Number,
clientHeight: Number,
clientWidth: Number,
clientTop: Number,
height: Number,
left: Number,
naturalHeight: Number,
naturalWidth: Number,
offsetHeight: Number,
offsetLeft: Number,
offsetTop: Number,
offsetWidth: Number,
scrollHeight: Number,
scrollLeft: Number,
scrollTop: Number,
scrollWidth: Number,
right: Number,
top: Number
width: Number
}
The bottom
, left
, right
, and top
properties are what you would expect from the result of element.getBoundingClientRect()
. naturalHeight
and naturalWidth
are properties that are native to img
elements, and for all non-img
elements they are coalesced with scrollHeight
and scrollWidth
, respectively.
These properties are retrieved on mount, but will also automatically update if the element is resized thanks to ResizeObserver. Please note that elements that do not support content (such as img
) are not supported by this resize listener because there is no content box to observe. If you need to support those elements, simply create a higher-order component that wraps that element in a div
and decorate that component.
Advanced usage
keys
(Array<string>|string)
The keys to listen for changes to. If not specified, all possible keys will be measured.
Examples:
import measure from "remeasure";
// pass a string value for a single property
const measureOnlyOffsetWidth = measure("offsetWidth");
const MyStatelessComponent = measureOnlyOffsetWidth(({ offsetWidth }) => {
return <div>Only offsetWidth is injected</div>;
});
// or an array of string values for multiple properties
@measure(["top", "height"])
class MyComponent extends Component {
render() {
const { top, height } = this.props;
return <div>Both the top and height props are injected</div>;
}
}
You can apply the keys one of two ways on the Measure
component:
// either as individual boolean properties
<Measured height>
{({height}) => {
return (
<div>I am {height} pixels in height.</div>
);
}}
</Measured>
// or as the "keys" prop
<Measured keys={['height']}>
{({height}) => {
return (
<div>I am {height} pixels in height.</div>
);
}}
</Measured>
Note that the properties will only be applied if they are set to true
(yes, you can actually toggle what properties are measured!).
options
Object
Allows customization of the measurements. Available options:
{
// value in milliseconds to debounce rerenders
debounce: Number,
// sets namespace for values to be passed into props on
namespace: String,
// should element rerender when resized
renderOnResize: Boolean = true
// should element rerender when the window is resized
renderOnWindowResize: Boolean = false
}
Example usage with the decorator:
// use them alone
@measure({ renderOnResize: false })
class MyComponent extends Component {
render() {
const { height, width } = this.props;
return <div>The height and width props will not update with resizes.</div>;
}
}
// or you can use them with keys
const MyStatelessComponent = measure(["height", "width"], {
debounce: 50,
namespace: "measurements"
})(({ measurements }) => {
return (
<div>
You can still pass options when you want to specify keys, as the second
parameter.
</div>
);
});
Example usage with the Measured
component:
<Measured debounce={500} namespace="measurements">
{({ measurements }) => {
return <div>My measurements: {JSON.stringify(measurements)}</div>;
}}
</Measured>
ref
Like any other component, you can access the Measured
component instance via the ref
, but when using the measure
decorator you will be accessing the Measured
HOC and not the original component. If you want to access the original component, it is available as the originalComponent
property on that ref
.
@measure.width
class Foo extends Component {
getProps() {
return this.props;
}
render() {
return <div>Use getProps to get my props!</div>;
}
}
...
class FooConsumer extends Component {
componentDidMount() {
console.log(this.foo); // Measured component
console.log(this.foo.originalComponent); // Foo component
console.log(this.foo.originalComponent.getProps()); // {bar: 'bar'}
}
render() {
<Foo
ref={(component) => {
this.foo = component;
}}
bar="bar"
/>
}
}
Convenience methods
For each key that is measured, a convenience function exists on the measure
decorator. Example:
@measure.width
class MyMeasuredComponent extends Component {
render() {
const { width } = this.props;
return <div>I have width of {width}.</div>;
}
}
Caveats
A couple things to keep in mind when using remeasure
:
Void tags cannot detect element resize
If children on a tag are considered invalid HTML (such as for <input/>
, <img/>
, etc), then the internal element resize detector cannot work. The easy solution to this is to update the component via props (on update, a recalculation of values is triggered).
Components may render twice on update
If you perform an update to the component props
or state
that also happens to change its dimensions, the component will update twice, once for the changes to props
/ state
, and again for the changes to its dimensions. This is because the component needs to render in the DOM before updated values can be calculated.
Support
remeasure
has been tested and confirmed to work on the following browsers:
- Chrome
- Firefox
- Opera
- Edge
- IE9+
remeasure
also works with universal / isomorphic applications.
Development
Standard stuff, clone the repo and npm i
to get the dependencies. npm scripts available:
build
=> builds the distributed JS withNODE_ENV=development
and with sourcemapsbuild-minified
=> builds the distributed JS withNODE_ENV=production
and minifiedcompile-for-publish
=> runs thelint
,test
,transpile
,dist
scriptsdev
=> runs the webpack dev server for the playgrounddist
=> runs thebuild
andbuild-minified
lint
=> runs ESLint against files in thesrc
folderprepublish
=> if in publish, runscompile-for-publish
test
=> run ava with NODE_ENV=testtest:watch
=> runstest
but with persistent watchertranspile
=> runs Babel against files insrc
to files inlib