При наборе текста в инпут, текст выходит за пределы инпута с левой стороны
Я увеличиваю ширину инпута в зависимоcти от его value. Так выходит, что когда идет набор текста, левая сторона немного уходит за границы инпута. Уже не в первый раз сталкиваюсь с этой проблемой. Подскажите, пожалуйста в чем дело? и как это исправить?
Мой код тут: codesandbox- https://codesandbox.io/s/upbeat-feistel-iqwqg?file=/src/App.js
Так выглядит проблема при наборе текста:

const Input = () => {
const [input, setInput] = useState("");
const spanEl = useRef(5);
const changeHandler = (event) => {
setInput(event.target.value);
};
return (
<div className="container">
<span className="hide" ref={spanEl}>
{input}
</span>
<input
className="input"
type="text"
value={input}
onChange={changeHandler}
style={{ width: spanEl.current.offsetWidth + "px" }}
/>
</div>
);
};
export default Input;
.container,
.input,
.hide {
font: inherit;
font-weight: 600;
}
.container {
display: flex;
flex-wrap: wrap;
width: 400px;
margin: 0 auto;
padding: 5px;
background-color: lightgrey;
min-height: 40px;
}
.input {
outline: none;
width: 2px;
}
.hide {
position: absolute;
height: 0;
overflow: hidden;
white-space: pre;
}
Ответы (2 шт):
Вы устанавливаете размеры текстового поля по размеру введенного текста,
но вы не учитываете то, что в реальности из области для ввода текста вычитаются:
- 2 пикселя внутреннего отступа слева
- 2 пикселя внутреннего отступа справа
Итого: область для ввода в реальности получается на 4 пикселя меньше, чем нужно, отчего ваш текст и съедается.
Просто учтите данные размеры и прибавьте их к ширине текстового поля.
const Input = () => {
const [input, setInput] = useState("");
const spanEl = useRef(5);
const changeHandler = (event) => {
setInput(event.target.value);
};
return (
<div className="container">
<span className="hide" ref={spanEl}>
{input}
</span>
<input
className="input"
type="text"
value={input}
onChange={changeHandler}
style={{ width: spanEl.current.offsetWidth + 4 + "px" }}
/>
</div>
);
};
export default Input;
.container,
.input,
.hide {
font: inherit;
font-weight: 600;
}
.container {
display: flex;
flex-wrap: wrap;
width: 400px;
margin: 0 auto;
padding: 5px;
background-color: lightgrey;
min-height: 40px;
}
.input {
outline: none;
width: 2px;
}
.hide {
position: absolute;
height: 0;
overflow: hidden;
white-space: pre;
}
И ваш текст будет помещаться в область для ввода
Либо уберите внутренние отступы у текстового поля, чтобы они не мешали вашим вычислениям:
.input {
padding: 0px; /* <-- */
outline: none;
width: 2px;
}
Это можно решить с помощью useEffect, подписавшись на изменения инпута.
import "./styles.css";
import { useState, useRef, useEffect } from "react";
import React from "react";
const defaultWidth = 5;
const Input = () => {
const [input, setInput] = useState("");
const [spanWidth, setSpanWidth] = useState(defaultWidth);
const spanEl = useRef(null);
const changeHandler = (event) => {
setInput(event.target.value);
};
useEffect(() => {
// Смотрим, если инпут имеет больше 1 символа, тогда делать рассчёт.
if (input.length > 0) {
setSpanWidth(spanEl.current.offsetWidth);
}
}, [input]);
return (
<div className="container">
<span className="hide" ref={spanEl}>
{input}
</span>
<input
className="input"
type="text"
value={input}
onChange={changeHandler}
style={{ width: spanWidth + "px" }}
/>
</div>
);
};
export default Input;

