我想要一个宽度能够适应其内容的输入。
我正在尝试实现类似问题的答案,但使用React:
import React, { useState } from 'react';
export default () => {
const [content, setContent] = useState('');
const [width, setWidth] = useState(0);
const changeHandler = evt => {
setContent(evt.target.value);
};
return (
<wrapper>
<span id="hide">{content}</span>
<input type="text" autoFocus style={{ width }} onChange={changeHandler} />
</wrapper>
);
};
问题是我不知道如何查询跨度的宽度,以便更改输入的宽度(使用
setWidth
)。
我怎样才能实现这个目标?
经过一番折腾,我找到了解决方案!
import React, { useState, useRef, useEffect } from 'react';
export default () => {
const [content, setContent] = useState('');
const [width, setWidth] = useState(0);
const span = useRef();
useEffect(() => {
setWidth(span.current.offsetWidth);
}, [content]);
const changeHandler = evt => {
setContent(evt.target.value);
};
return (
<wrapper>
<span id="hide" ref={span}>{content}</span>
<input type="text" style={{ width }} autoFocus onChange={changeHandler} />
</wrapper>
);
};
为了获得对
#hide
跨度的参考,我使用了useRef
。然后,可以通过 width
内部定义的函数来更新 useEffect
状态变量,每次 content
发生变化时都会调用该函数。
我还必须将
display: none
的 CSS 中的 #hide
切换为 position: absolute
和 opacity: 0
,否则 targetRef.current.offsetWidth
将始终评估为 0。
这是一个工作演示。
嗯,这很有趣! 我尝试了一些不同的想法,但没有一个能完美地工作——尤其是如果它们是用一种有点受人尊敬的代码编写的。
但是我发现了这篇文章并决定尝试一下。 https://stackoverflow.com/a/43488899/3293843
我确信它有缺陷,例如,除非我使用等宽字体,否则它确实会很有趣。但也许有一些 CSS 技巧可以解决这个问题?
// Normally I'd go for ES6 imports, but to make it run as a StackOverflow snippet I had to do it this way
const { useState, useRef } = React;
const GrowingInput = () => {
const [width, setWidth] = useState(0);
const changeHandler = evt => {
setWidth(evt.target.value.length);
};
return (
<input style={{ width: width +'ch'}} type="text" autoFocus onChange={changeHandler} />
)
};
const App = () => {
return (
<p>Lorem ipsum {<GrowingInput />} egestas arcu.</p>
);
};
// Render it
ReactDOM.render(<App />, document.getElementById("react"));
input {
font-family: Courier;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
您是否考虑过使用
contenteditable
来代替?
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content
这是我找到的最简单的解决方案。
您创建一个将在更改时使用的函数
const handleChangeAndSize = (ev: ChangeEvent<HTMLInputElement>) => {
const target = ev.target;
target.style.width = '60px';
target.style.width = `${target.scrollWidth}px`;
handleChange(ev);
};
然后将其用作组件中的常规函数
<input type='text' onChange={handleChangeAndSize}/>
style.width = 60px 将允许在缩小时调整输入大小,并且 target.scrollWidth 将监视 x 轴上的“可滚动宽度”并将其设置为宽度。
在react中发现了使用Refs的技巧。
style={{ width: inputRef.current ? inputRef.current.value.length + 'ch' : 'auto' }}
并为元素设置
ref={inputRef}
。
请记住在 CSS 中设置输入的最小宽度。
不使用额外隐藏元素的另一种选择:
const GrowingInput = () => {
const inputRef = React.useRef(null);
const handleChange = () => {
inputRef.current.style.width = "0";
inputRef.current.style.width = `${inputRef.current.scrollWidth}px`;
};
return <input ref={inputRef} style={{width: 0}} autoFocus onChange={handleChange} />
};
const App = () => <p>Lorem ipsum {<GrowingInput />} egestas arcu. </p>;
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>