足够简单的问题: 我正在尝试创建与提供给它们的文本一样大的输入。
沙箱:https://codesandbox.io/s/long-snowflake-6u13n?file=/src/Test.jsx
我的设计意图是动态生成输入,然后允许用户具有特定于每个输入的样式,这些样式在视觉上有助于根据外部事件分解每个句子。但在我继续前进之前,我的输入容器仅与其中的文本一样大,这一点非常重要。
为什么不使用文本区域? -- 我有每个句子特有的数据,我想为其创建独特的样式。
有什么想法吗?
这是一种来自纯 HTML/CSS 和工作片段的方法,将
span
中键入的值隐藏在 input
absolute
中设置的 position
后面。 CSS 可以使 span
和 input
匹配相同的长度/宽度。拉伸/折叠父母 (label
) 将完成工作。
感谢@silvenon,您还可以在代码片段下方找到反应示例
var val = document.querySelector('#test');
let tpl = document.querySelector('#tpl');
let text = val.value;
tpl.textContent= text;
val.addEventListener("input", function () {// onchange ...
let text= val.value;
//console.log(text);
tpl.textContent= text;
});
label {
display: inline-block;
position: relative;
min-width: 2em;
min-height: 1.4em;
}
#tpl {
white-space: pre;
/* max-width : could be wised to set a maximum width and overflow:hidden; */
}
#test {
font-family: inherit;
font-size: inherit;
position: absolute;
vertical-align: top;
top: 0;
left: 0;
width: 100%;
background: white;
}
<label><span id="tpl"></span><input id='test' value="Some test to try" ></label>
感谢 @silvenon,您可能会找到该代码的 React 示例。
const SentenceInput = styled.input`
padding: 0;
margin: 0;
border: none;
border: 1px solid black;
/* added styles */
font-family: inherit;
font-size: inherit;
position: absolute;
vertical-align: top;
top: 0;
left: 0;
width: 100%;
background: white;
`
const Label = styled.label`
display: inline-block;
position: relative;
min-width: 2em;
min-height: 1.4em;
`
const Template = styled.span`
white-space: pre;
/* max-width : could be wised to set a maximum width and overflow:hidden; */
`
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
return (
<Label>
<Template>{value}</Template>
<SentenceInput
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value)
}}
/>
</Label>
)
}
<input>
size
属性可用于设置宽度。它的工作方式与 ch 单位相同,例如<input size="10">
的宽度为 10ch
。与 ch
单位一样,它可以完美地与等宽字体配合使用,但对于按比例间隔的字体来说只是一个近似值。
要使用按比例间隔的字体获得更准确的测量,您可以使用
scrollWidth
元素本身的 <input>
属性来计算宽度。技巧是先将值设置为“auto”,以在设置值之前捕获内容宽度。如果您想完全适合内容,请将输入 padding
设置为零。
import React, { useEffect, useRef } from "react";
const data = [
{ sentence: "Hello world, how are you?" },
{ sentence: "Sometimes in rains in the summer." },
{
sentence:
"Depending where you currently live, it my even rain in the winter."
}
];
const SentenceInput = (props) => {
const { value, onChange } = props;
const inputRef = useRef(null);
useEffect(() => {
const input = inputRef.current;
input.style.width = 'auto';
input.style.width = `${input.scrollWidth}px`;
});
return <input ref={inputRef} type="text" value={value} onChange={onChange} />;
};
const Test = () => {
const handleChange = () => {};
return (
<div className="App">
{data.map(({ sentence }, i) => {
return (
<SentenceInput key={i} value={sentence} onChange={handleChange} />
);
})}
</div>
);
};
export default Test;
如果字体是等宽字体,则可以使用
ch
单位,否则字符宽度会有所不同。我会通过渲染一个包含相同文本的内联元素、测量它并在每次输入字段值发生变化时立即隐藏它来解决这个问题。
为此,最好创建一个单独的组件来渲染句子,我们称之为
Sentence
:
const Test = () => {
return (
<div className="App">
{value.map(({ sentence }, i) => {
return (
<Sentence
initialValue={sentence}
key={i}
/>
);
})}
</div>
);
};
Test
会传递初始值,然后Sentence
将继续保持自己的状态:
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
return (
<SentenceInput
type="text"
value={value}
onChange={(event) => {
setValue(event.target.value)
}}
/>
)
}
接下来,我将添加一个
span
元素作为测量器元素,其中文本的样式应与输入元素中的样式相同,以便测量结果准确。在 Chrome 中的示例中,这意味着将字体大小设置为 13.3333px
。
现在最棘手的部分,我们需要结合
useEffect
和 useLayoutEffect
; useEffect
将使测量器可见,然后 useLayoutEffect
将测量它并隐藏它
这是结果:
const Sentence = ({ initialValue }) => {
const [value, setValue] = React.useState(initialValue)
const [visible, setVisible] = React.useState(false)
const [width, setWidth] = React.useState('auto')
const measurer = React.useRef(null)
React.useEffect(() => {
setVisible(true)
}, [value])
React.useLayoutEffect(() => {
if (visible) {
const rect = measurer.current.getBoundingClientRect()
setWidth(rect.width)
setVisible(false)
}
}, [visible])
return (
<>
<span
ref={measurer}
style={{ fontSize: '13.3333px' }}
>
{visible && value}
</span>
<SentenceInput
type="text"
value={value}
style={{ width: width + 1 }}
onChange={(event) => {
setValue(event.target.value)
}}
/>
</>
)
}
我将
1px
添加到计算的 width
中,因为它似乎删除了输入字段中的小水平滚动。
您可以按照自己想要的方式进一步调整,例如当它达到视口宽度时应该如何表现。
使用
sx
覆盖 css 类
<TextField
sx={{
'& .MuiInputBase-root': {
color: 'white',
paddingTop: '10px'
},
'& .css-uhyr2s-MuiInputBase-root-MuiInput-root::before': {
border: '0px'
},
'& .css-uhyr2s-MuiInputBase-root-MuiInput-root:hover:not(.Mui-disabled, .Mui-error):before': {
border: '0px'
},
'& .css-uhyr2s-MuiInputBase-root-MuiInput-root::after': {
border: '0px'
},
'& textarea:focus': {
boxShadow: '0px 0px'
}
}}
/>