Как узнать влезает ли текст в контейнер или будет переноситься на следующую строку?

У меня есть React-компонент, который в качестве children принимает текст и должен принимать разные стили в зависимости от того, влезает ли текст в контейнер или будет переноситься на следующую строку. Возможно ли как-то управлять стилями в зависимости от наполнения контейнера?

Пример:

import React from 'react'
import S from './style.module.css'

function Component(props) {
  const textIsOverflow = ... // какое-то вычисление?

  return (
    <div className={textIsOverflow ? S.wrapperOverflow : S.wrapper}>
      <div classname={S.caption}>
        {props.children}
      </div>
    </div>
  )
}

Ответы (1 шт):

Автор решения: xydope

Чтобы не зависеть от каких-то конкретных цифр с высотой строки, можно использовать скрытый элемент, который будет иметь такие же стили, как и главные элемент, но в нем будет всегда только 1 строка. Речь идет о стилях, которые влияют на вместимость текста: размер, отступы, ширина элемента и т.п.

Дальше можно использовать useRef и useEffect, чтобы сравнивать высоту блока и определять необходимый класс.

В примере ниже главный div редактируемый, как только введенный текст превысит высоту в 1 строку, его цвет изменится на розовый (в обратную сторону также работает).

В остальном см. комментарии по коду

function Component(props) {
  //стейт, в котором хранится имя класса
  const [divClassName, setDivClassName] = React.useState("single-string");
  
  //стейт с текстом для примера
  const [text, setText] = React.useState("Editable field");
  
  //реф на основной элемент, в который вставляете текст
  const visibleRef = React.useRef();
  //реф на скрытый элемент, в котором всегда 1 строка.
  const hiddenRef = React.useRef();

  React.useEffect(() => {
    //при рендере сравниваем высоту видимого блока и скрытого блока с постоянной высотой в 1 строку
    const currentClassName =
      visibleRef.current.offsetHeight > hiddenRef.current.offsetHeight
        ? "multi-string"
        : "single-string";

    //определеяем нужно ли менять имя класса
    const isNeedToggle = divClassName !== currentClassName;
    
    //если нужно, то меняем
    if (isNeedToggle) {
      setDivClassName(currentClassName);
    }
  }, [text]);

  return (
    <div className="parrent">
      <div ref={hiddenRef} className="text hidden">
        single string
      </div>
      <div
        ref={visibleRef}
        contentEditable
        className={`text ${divClassName}`}
        onInput={(event) => {
          setText(event.currentTarget.textContent);
        }}
      >
        {text}
      </div>
    </div>
  );
}

ReactDOM.render(<Component />, document.getElementById("root"));
.parrent {
  position: relative;
}

.hidden {
  position: absolute;
  z-index: -1;
  visibility: hidden;
}

.text {
  width: 200px;
}

.single-string {
  background: lightgreen;
}

.multi-string {
  background: pink;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

→ Ссылка