ReactJS-获取元素的高度

在 React 呈现一个元素之后,如何得到该元素的 Height?

超文本标示语言

<div id="container">
<!-- This element's contents will be replaced with your component. -->
<p>
jnknwqkjnkj<br>
jhiwhiw (this is 36px height)
</p>
</div>

ReactJS

var DivSize = React.createClass({


render: function() {
let elHeight = document.getElementById('container').clientHeight
return <div className="test">Size: <b>{elHeight}px</b> but it should be 18px after the render</div>;
}
});


ReactDOM.render(
<DivSize />,
document.getElementById('container')
);

结果

Size: 36px but it should be 18px after the render

它计算渲染前的容器高度(36px)。我想得到渲染后的高度。在这种情况下,正确的结果应该是18px。Jsfiddle

386069 次浏览

参见 这个小提琴(实际上更新了您的)

你需要连接到运行在渲染方法之后的 componentDidMount。在那里,你可以得到元素的实际高度。

var DivSize = React.createClass({
getInitialState() {
return { state: 0 };
},


componentDidMount() {
const height = document.getElementById('container').clientHeight;
this.setState({ height });
},


render: function() {
return (
<div className="test">
Size: <b>{this.state.height}px</b> but it should be 18px after the render
</div>
);
}
});


ReactDOM.render(
<DivSize />,
document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>


<div id="container">
<p>
jnknwqkjnkj<br>
jhiwhiw (this is 36px height)
</p>
<!-- This element's contents will be replaced with your component. -->
</div>

您还可能希望对元素使用参考文献,而不是使用 document.getElementById,这只是一个稍微健壮一些的东西。

下面是使用 裁判的最新 ES6示例。

请记住,我们必须使用 反应类组件,因为我们需要访问 Lificycle 方法 componentDidMount(),因为我们只能在元素在 DOM 中呈现之后确定它的高度。

import React, {Component} from 'react'
import {render} from 'react-dom'


class DivSize extends Component {


constructor(props) {
super(props)


this.state = {
height: 0
}
}


componentDidMount() {
const height = this.divElement.clientHeight;
this.setState({ height });
}


render() {
return (
<div
className="test"
ref={ (divElement) => { this.divElement = divElement } }
>
Size: <b>{this.state.height}px</b> but it should be 18px after the render
</div>
)
}
}


render(<DivSize />, document.querySelector('#container'))

您可以在这里找到正在运行的示例: https://codepen.io/bassgang/pen/povzjKw

对于那些对使用 react hooks感兴趣的人,这可能有助于您开始使用。

import React, { useState, useEffect, useRef } from 'react'


export default () => {
const [height, setHeight] = useState(0)
const ref = useRef(null)


useEffect(() => {
setHeight(ref.current.clientHeight)
})


return (
<div ref={ref}>
{height}
</div>
)
}

我找到了有用的 npm 软件包 https://www.npmjs.com/package/element-resize-detector

优化的跨浏览器元素大小调整监听器。

可以与反应组件或功能组件一起使用(特别适用于反应挂钩)

如果你需要调整窗口大小的事件,这里还有一个:

class DivSize extends React.Component {


constructor(props) {
super(props)


this.state = {
width: 0,
height: 0
}
this.resizeHandler = this.resizeHandler.bind(this);
}


resizeHandler() {
const width = this.divElement.clientWidth;
const height = this.divElement.clientHeight;
this.setState({ width, height });
}


componentDidMount() {
this.resizeHandler();
window.addEventListener('resize', this.resizeHandler);
}


componentWillUnmount(){
window.removeEventListener('resize', this.resizeHandler);
}


render() {
return (
<div
className="test"
ref={ (divElement) => { this.divElement = divElement } }
>
Size: widht: <b>{this.state.width}px</b>, height: <b>{this.state.height}px</b>
</div>
)
}
}


ReactDOM.render(<DivSize />, document.querySelector('#container'))

密码笔

另一种解决方案是,如果您想同步检索 React 元素的大小,而不需要可见地呈现该元素,那么可以使用 Ref = “ https://reactjs.org/docs/response-dom-server.html”rel = “ nofollow noReferrer”> ReactDOMServer REL = “ nofollow noReferrer”> DOMParser

当使用 < a href = “ https://github.com/bvaughn/response-window”rel = “ nofollow norefrer”> response-window (< a href = “ https://github.com/bvaughn/response-Virtual”rel = “ nofollow noReferrer”> response-Virtual )时,我使用这个函数来获得我的列表项渲染器的高度,而不必为 < a href = “ https://response-window.now.sh/#/api/FixedSizeList”rel = “ nofollow noReferrer”> FixedSizeList 硬编码所需的 itemSize道具。

Utilities.js:

/**
* @description Common and reusable functions
*
* @requires react-dom/server
*
* @public
* @module
*
*/
import ReactDOMServer from "react-dom/server";


/**
* @description Retrieve the width and/or heigh of a React element without rendering and committing the element to the DOM.
*
* @param {object} elementJSX - The target React element written in JSX.
* @return {object}
* @public
* @function
*
* @example
*
* const { width, height } = getReactElementSize( <div style=\{\{ width: "20px", height: "40px" }} ...props /> );
* console.log(`W: ${width}, H: ${height});  // W: 20, H: 40
*
*/
const getReactElementSize = (elementJSX) => {


const elementString = ReactDOMServer.renderToStaticMarkup(elementJSX);
const elementDocument = new DOMParser().parseFromString(elementString, "text/html");
const elementNode = elementDocument.getRootNode().body.firstChild;


const container = document.createElement("div");
const containerStyle = {


display: "block",
position: "absolute",
boxSizing: "border-box",
margin: "0",
padding: "0",
visibility: "hidden"
};


Object.assign(container.style, containerStyle);


container.appendChild(elementNode);
document.body.appendChild(container);


const width = container.clientWidth;
const height = container.clientHeight;


container.removeChild(elementNode);
document.body.removeChild(container);


return {


width,
height
};
};


/**
* Export module
*
*/
export {


getReactElementSize
};

用钩子:

如果您的内容维度在加载后发生更改,那么这个答案将非常有用。

Onreadystatechange : 属于元素或 HTML 文档的数据的加载状态更改时发生。当页面内容的加载状态发生更改时,会在 HTML 文档上触发 onreadystatechange 事件。

import {useState, useEffect, useRef} from 'react';
const ref = useRef();
useEffect(() => {
document.onreadystatechange = () => {
console.log(ref.current.clientHeight);
};
}, []);

我试图与一个 youtube 视频播放器嵌入其尺寸可能会改变后加载。

我2020年(或2019年)的答案

import React, {Component, useRef, useLayoutEffect} from 'react';
import { useDispatch } from 'react-redux';
import { Toast, ToastBody, ToastHeader } from 'reactstrap';


import {WidgetHead} from './WidgetHead';


export const Widget = ({title, toggle, reload, children, width, name}) => {
let myself = useRef(null);
const dispatch = useDispatch();
useLayoutEffect(()=>{
if (myself.current) {
const height = myself.current.clientHeight
dispatch({type:'GRID_WIDGET_HEIGHT', widget:name, height})
}
}, [myself.current, myself.current?myself.current.clientHeight:0])


return (
<Toast innerRef={myself}>
<WidgetHead title={title}
toggle={toggle}
reload={reload} />
<ToastBody>
{children}
</ToastBody>
</Toast>
)
}

让我们想象一下这里缺少了什么(WidgetHead) ,reactstrap是你可以在 npm 上找到的: 用 ref替换 innerRef作为遗留 dom 元素(比如 <div>)。

用效果或者用布局效果

据说最后一个变化是同步的

第二个参数

第二个参数是一个数组,在执行第一个参数中的函数之前会对其进行检查。

我用过

[ myself. current,myself. current? myself. current.clientHeight: 0]

因为 myself. current 在呈现之前是 null,这是一个不需要检查的好东西,所以我想检查 myself.current.clientHeight末尾的第二个参数是否有更改。

我在这里正在解决的问题(或正在尝试解决的问题)

我在这里解决的问题,窗口小部件在网格上改变其高度自己的意愿,网格系统应该是 足够有弹性的反应(https://github.com/STRML/react-grid-layout)。

与使用 document.getElementById(...)不同,一个更好的(最新的)解决方案是使用 React UseRef钩子,该钩子存储对组件/元素的引用,并结合使用 使用效果钩子,该钩子在组件呈现时触发。

import React, {useState, useEffect, useRef} from 'react';


export default App = () => {
const [height, setHeight] = useState(0);
const elementRef = useRef(null);


useEffect(() => {
setHeight(elementRef.current.clientHeight);
}, []); //empty dependency array so it only runs once at render


return (
<div ref={elementRef}>
{height}
</div>
)
}

它可能显示0。 setTimeout 有助于获得正确的值并更新状态。

import React, { useState, useEffect, useRef } from 'react'
    

export default () => {
const [height, setHeight] = useState(0)
const ref= useRef(null)
    

useEffect(() => {
if(elemRef.current.clientHeight){
setTimeout(() => {
setHeight(ref.current.clientHeight)
}, 1000)
}
})
    

return (
<div ref={ref}>
{height}
</div>
)
}

这里有一个从 https://swizec.com/blog/usedimensions-a-react-hook-to-measure-dom-nodes改进来的可重复使用的钩子:

import { useState, useCallback, useEffect } from 'react';


function getDimensionObject(node) {
const rect = node.getBoundingClientRect();


return {
width: rect.width,
height: rect.height,
top: 'x' in rect ? rect.x : rect.top,
left: 'y' in rect ? rect.y : rect.left,
x: 'x' in rect ? rect.x : rect.left,
y: 'y' in rect ? rect.y : rect.top,
right: rect.right,
bottom: rect.bottom
};
}


export function useDimensions(data = null, liveMeasure = true) {
const [dimensions, setDimensions] = useState({});
const [node, setNode] = useState(null);


const ref = useCallback(node => {
setNode(node);
}, []);


useEffect(() => {
if (node) {
const measure = () =>
window.requestAnimationFrame(() =>
setDimensions(getDimensionObject(node))
);
measure();


if (liveMeasure) {
window.addEventListener('resize', measure);
window.addEventListener('scroll', measure);


return () => {
window.removeEventListener('resize', measure);
window.removeEventListener('scroll', measure);
};
}
}
}, [node, data]);


return [ref, dimensions, node];
}

实施:

import { useDimensions } from '../hooks';


// Include data if you want updated dimensions based on a change.
const MyComponent = ({ data }) => {
const [
ref,
{ height, width, top, left, x, y, right, bottom }
] = useDimensions(data);


console.log({ height, width, top, left, x, y, right, bottom });


return (
<div ref={ref}>
{data.map(d => (
<h2>{d.title}</h2>
))}
</div>
);
};


我发现其他答案与反应钩没有正确更新调整大小。

在周围搜索之后,我发现 这篇博文提供了一个可以观察调整事件的工作反应钩:

TL; DR 在这里:

npm install --save resize-observer-polyfill

// useResizeObserver.js
import { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';


const useObserver = ({ callback, element }) => {


const current = element && element.current;


const observer = useRef(null);


useEffect(() => {
// if we are already observing old element
if (observer && observer.current && current) {
observer.current.unobserve(current);
}
const resizeObserverOrPolyfill =  ResizeObserver;
observer.current = new resizeObserverOrPolyfill(callback);
observe();


return () => {
if (observer && observer.current && element &&
element.current) {
observer.current.unobserve(element.current);
}
};
}, [current]);


const observe = () => {
if (element && element.current && observer.current) {
observer.current.observe(element.current);
}
};


};


useObserver.propTypes = {
element: PropTypes.object,
callback: PropTypes.func,
};


export default useObserver;

然后是组件中的一个用法示例:

// shape.js
import React, { useEffect, useState, useRef } from 'react';
import useResizeObserver from 'path/to/useResizeObserver.js';


const Shape = () => {
const [height, setHeight] = useState(0);
const svgRef = useRef(null);


const doHeightAdjustment = () => {
setHeight(svgRef.current.clientHeight);
};


useResizeObserver({callback: doHeightAdjustment, element: svgRef});


return (
<div ref={svgRef} style=\{\{ height: '100vh' }}>
{height}
</div>
);
};


export default Shape;


你也可以使用 getBoundingClientRect()得到高度,宽度。

const [width, setWidth] = useState(0);


useEffect(() => {
const element = document.getElementById('element-id');
if (element) {
setWidth(element.getBoundingClientRect().width); // or height
}
}, []);

使用 useMeasure作为自定义钩子(打字稿,SSR,钩子) :

import { useEffect, useRef, useState } from 'react';


interface ContainerSize {
width: number;
height: number;
}


type UseMeasureArgs = () => {
ref: React.RefObject<HTMLDivElement>;
size: ContainerSize;
windowSize: ContainerSize;
};


const initSize: ContainerSize = { width: 0, height: 0 };


const useMeasure: UseMeasureArgs = () => {
const ref = useRef<HTMLDivElement>(null);
const [size, setSize] = useState<ContainerSize>(initSize);
const [windowSize, setWindowSize] = useState<ContainerSize>(initSize);


useEffect(() => {
if (ref.current) {
setSize({ width: ref.current.offsetWidth, height: ref.current.offsetHeight });
}
if (typeof window !== 'undefined') {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
}, []);


return { ref, size, windowSize };
};


export default useMeasure;


你可以用这个钩子

import useMeasure from "react-use-measure";
const [ref, {height}] = useMeasure()

剩下的代码

<div ref={ref} id="container">
</div>

然后您可以访问您想要的高度,并且每次它的大小变化时都会更新它