Как узнать влезает ли текст в контейнер или будет переноситься на следующую строку?
У меня есть 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 шт):
Чтобы не зависеть от каких-то конкретных цифр с высотой строки, можно использовать скрытый элемент, который будет иметь такие же стили, как и главные элемент, но в нем будет всегда только 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>