ReactJS - Bir elemanın yüksekliğini al


121

React o öğeyi oluşturduktan sonra bir elemanın Yüksekliğini nasıl alabilirim?

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

SONUÇ

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

Oluşturmadan önce konteyner yüksekliğini hesaplıyor (36px). Renderden sonra yüksekliği almak istiyorum. Bu durumda doğru sonuç 18 piksel olmalıdır. jsfiddle


1
Bu bir tepki sorusu değil, bir Javascript ve DOM sorusudur. Öğenizin son yüksekliğini bulmak için hangi DOM olayını kullanmanız gerektiğini bulmaya çalışmalısınız. Olay işleyicide, setStateyükseklik değerini bir durum değişkenine atamak için kullanabilirsiniz .
mostruash

Yanıtlar:


28

Bu kemanı görün (aslında sizinki güncellendi)

İçine Sen kanca gereken componentDidMountyöntem hale getirdikten sonra koşması. Orada, elementin gerçek yüksekliğini elde edersiniz.

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>

16
doğru değil. aşağıdaki Paul Vincent
Beigang'ın

1
IMHO, bir bileşen kabının adını bilmemeli
Andrés

157

Aşağıda, ref kullanan güncel bir ES6 örneği verilmiştir .

Lifecycle yöntemine erişmemiz gerektiğinden bir React sınıfı bileşeni kullanmamız gerektiğini unutmayın componentDidMount()çünkü bir elementin yüksekliğini ancak DOM'da oluşturulduktan sonra belirleyebiliriz.

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

Çalışan örneği burada bulabilirsiniz: https://codepen.io/bassgang/pen/povzjKw


6
Bu çalışıyor ancak Airbnb stil kılavuzu için birkaç eslint hatası alıyorum: <div ref={ divElement => this.divElement = divElement}>{children}</div>"[eslint] Ok işlevi atamayı geri döndürmemelidir this.setState({ height });. did-mount-set-state) ". Stil kılavuzuna uygun bir çözümü olan var mı?
Timo

7
Atamayı parantez içine sarın, böylece (bir ifade yerine) bir işlev olarak davranır, şöyle kiref={ divElement => {this.divElement = divElement}}
teletypist

3
Bu kabul edilen cevap olabilir :) Ayrıca, eleman güncellendikten sonra elemanın boyutunu almak istiyorsanız yöntemi de kullanabilirsiniz componentDidUpdate.
jjimenez

4
bu örnekle componentDidMount içinde this.setState () 'i çağırmanın bir alternatifi var mı?
danjones_mcr

3
Güzel çözüm. Ancak duyarlı tasarımlar için çalışmaz. Masaüstü için üstbilgi yüksekliği 50 piksel ise ve kullanıcı pencere boyutunu küçültür ve yükseklik 100 piksel olursa, bu çözüm güncellenmiş yüksekliği almayacaktır. Değiştirilen efektleri almak için sayfayı yenilemeleri gerekir.
TheCoder

100

Kullanmak isteyenler için bu react hooks, başlamanıza yardımcı olabilir.

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

11
Cevabınız için teşekkürler - başlamama yardımcı oldu. Yine de 2 soru: (1) Bu düzen ölçme görevleri için useLayoutEffectyerine kullanmalı mıyız useEffect? Belgeler, yapmamız gerektiğini gösteriyor gibi. Buradaki "ipucu" bölümüne bakın: reactjs.org/docs/hooks-effect.html#detailed-explanation (2) Sadece bir alt öğeye referansa ihtiyaç duyduğumuz bu durumlar için, useRefyerine kullanmalı mıyız createRef? Dokümanlar useRef, bu kullanım örneği için daha uygun olduğunu gösteriyor . Bakınız: reactjs.org/docs/hooks-reference.html#useref
Jason Frank

Önerdiğim iki öğeyi kullanan bir çözüm uyguladım. İyi çalışıyor gibi görünüyorlar, ancak bu seçimlerin bazı dezavantajlarını biliyor olabilirsiniz? Yardımınız için teşekkürler!
Jason Frank

4
@JasonFrank İyi sorular. 1) Hem useEffect hem de useLayoutEffect, düzen ve boyamadan sonra ateşlenecektir. Ancak, useLayoutEffect bir sonraki boyamadan önce eşzamanlı olarak çalışacaktır. Gerçekten görsel tutarlılığa ihtiyacınız varsa useLayoutEffect'i kullanın, aksi takdirde useEffect yeterli olacaktır. 2) Bu konuda haklısın! İşlevsel bir bileşende createRef, bileşen her işlendiğinde yeni bir referans oluşturur. UseRef'i kullanmak daha iyi bir seçenektir. Cevabımı güncelleyeceğim. Teşekkürler!
Bay 14

Bu, kancalı bir referansı nasıl kullanacağımı anlamama yardımcı oldu. useLayoutEffectBuradaki diğer yorumları okuduktan sonra devam ettim . İyi çalışıyor gibi görünüyor. :)
GuiDoody

1
Bileşenimin boyutlarını ayarlıyordum useEffect(() => setDimensions({width: popup.current.clientWidth, height: popup.current.clientHeight}))ve IDE'm (WebStorm) bana şunu önerdi: ESLint: React Hook useEffect 'setDimensions' çağrısı içeriyor. Bir bağımlılık listesi olmadan, bu sonsuz bir güncelleme zincirine yol açabilir. Bunu düzeltmek için, useEffect Hook'a ikinci argüman olarak [] geçirin. (tepki-kancalar / ayrıntılı-deps) , bu yüzden []ikinci argüman olarak geçuseEffect
Akshdeep Singh

9

Ayrıca kullanmak yerine öğe üzerinde ref kullanmak istersiniz document.getElementById, bu sadece biraz daha sağlam bir şeydir.


@doublejosh temelde haklısınız ama örneğin karıştırmanız gerekiyorsa angularJsve reactbirinden diğerine geçerken ne yapmalısınız ? Doğrudan bir DOM manipülasyonunun gerçekten gerekli olduğu durumlar vardır
messerbill

3

2020'nin (veya 2019) cevabı

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

Burada eksik olan şey için hayal gücünüzü kullanın (WidgetHead), reactstrapnpm'de bulabileceğiniz bir şeydir: eski bir dom öğesi için innerRefile değiştirin ref(örneğin a <div>).

useEffect veya useLayoutEffect

Sonuncunun değişiklikler için senkronize olduğu söyleniyor

useLayoutEffect(veya useEffect) ikinci argüman

İkinci argüman bir dizidir ve ilk argümandaki fonksiyon çalıştırılmadan önce kontrol edilir.

kullandım

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

çünkü kendim.current, renderlemeden önce null ve bu kontrol etmemek için iyi bir şey, sondaki ikinci parametre myself.current.clientHeightdeğişiklikleri kontrol etmek istediğim şey.

burada çözdüğüm (veya çözmeye çalıştığım)

Burada kendi iradesiyle yüksekliğini değiştiren bir ızgarada widget sorununu çözüyorum ve ızgara sistemi tepki verecek kadar esnek olmalıdır ( https://github.com/STRML/react-grid-layout ).


1
harika bir cevap, ancak daha basit bir örnekten faydalanırdı. Temelde whiteknight'ın cevabı, ancak useLayoutEffectref ile bağlamak için gerçek div ile.
bot 19

2

Kullanmak yerine, document.getElementById(...)daha iyi (güncel) bir çözüm, bileşene / öğeye bir referansı depolayan React useRef kancasını kullanmaktır ve bu kancayı bir useEffect kancasıyla birleştirerek bileşen oluşturmalarında tetikler .

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

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

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

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

1

Kancalarla kullanım:

Yüklendikten sonra içerik boyutunuz değişirse bu yanıt yardımcı olacaktır.

onreadystatechange : Bir öğeye veya bir HTML belgesine ait olan verilerin yükleme durumu değiştiğinde gerçekleşir. Onreadystatechange olayı, sayfanın içeriğinin yükleme durumu değiştiğinde bir HTML belgesinde tetiklenir.

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

Yüklendikten sonra boyutları değişebilen bir youtube video oynatıcısı yerleştirerek çalışmaya çalışıyordum.



0

Pencere yeniden boyutlandırma olayına ihtiyacınız varsa bir tane daha:

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

kod kalemi


0

Alternatif bir çözüm, bir React öğesinin boyutunu eşzamanlı olarak, öğeyi görsel olarak oluşturmak zorunda kalmadan almak istemeniz durumunda, ReactDOMServer ve DOMParser'ı kullanabilirsiniz .

Kullanırken bir benim liste Oluşturucuya yüksekliğini almak için bu işlevi kullanın tepki-pencere ( -sanallaştırılmış tepki yerine gerekli kod olarak girme) itemSizebir için pervane FixedSizeList .

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
};
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.