ReactJS - 获取元素的高度

时间:2016-02-02 12:30:14

标签: javascript reactjs

如何在React呈现该元素后获取元素的高度?

HTML

<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')
);

RESULT

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

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

https://jsfiddle.net/69z2wepo/29800/

14 个答案:

答案 0 :(得分:111)

以下是使用ref的最新ES6示例。

请记住,我们必须使用React class component,因为我们需要访问Lifecycle方法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://www.webpackbin.com/bins/-KkP1HhC_tW4FeoYSuDe

答案 1 :(得分:21)

请参阅this小提琴(实际更新了你的)

您需要挂钩在render方法之后运行的componentDidMount。在那里,你得到元素的实际高度。

答案 2 :(得分:19)

对于有兴趣使用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>
  )
}

答案 3 :(得分:8)

您还希望在元素上使用引用而不是使用document.getElementById,它只是一个稍微强大的东西。

答案 4 :(得分:6)

一个更好的(最新的)解决方案不是使用{{ PRODUCT CATEGORY }},而是使用一个React useRef钩子,该钩子存储一个对组件/元素的引用,并结合了一个useEffect钩子,它会在组件渲染时触发。

document.getElementById(...)

答案 5 :(得分:5)

我的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上可以找到的:将innerRef替换为ref来替换旧版dom元素(例如{1}}。

useEffect或useLayoutEffect

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

<div>(或useLayoutEffect)第二个参数

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

我用过

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

因为我自己。渲染前current为null,所以最好不要检查,useEffect结尾的第二个参数是我要检查的更改。

我在这里要解决(或尝试解决)

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

答案 6 :(得分:1)

使用挂钩:

如果您的内容尺寸在加载后发生变化,此答案将很有帮助。

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

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

我正尝试使用嵌入的youtube视频播放器,加载后尺寸可能会发生变化。

答案 7 :(得分:0)

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

针对元素的优化的跨浏览器调整大小侦听器。

可以与React组件或功能组件一起使用(对React钩子特别有用)

答案 8 :(得分:0)

如果您需要窗口调整大小事件,这里是另外一个:

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'))

code pen

答案 9 :(得分:0)

另一种解决方案,如果您想同步获取React元素的大小而不必显式呈现该元素,则可以使用 ReactDOMServer {{3 }}

使用 DOMParser react-window )时,我可以使用此功能来获取列表项渲染器的高度,而不必进行硬编码 react-virtualized 所需的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
};

答案 10 :(得分:0)

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

const nodeExternals = require("webpack-node-externals")

...

const webpackConfig = {
  ...
  externals: [nodeExternals({
    allowlist: ['saslprep']
  })],
  ...
}

答案 11 :(得分:0)

这是一个很好的可重用的钩子,它经过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>
  );
};

答案 12 :(得分:0)

我发现 React 钩子的其他答案在调整大小时没有正确更新。

在四处搜索后,我发现 this blog post 提供了一个观察调整大小事件的有效 React 钩子:

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;

答案 13 :(得分:0)

您还可以使用 getBoundingClientRect() 获取高度、宽度。

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

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