反应:如何使输入仅与提供的文本量一样宽?
Posted
技术标签:
【中文标题】反应:如何使输入仅与提供的文本量一样宽?【英文标题】:React: how to make an input only as wide as the amount of text provided? 【发布时间】:2021-01-13 11:38:17 【问题描述】:简单的问题: 我正在尝试创建与提供给它们的文本一样大的输入。
沙盒:https://codesandbox.io/s/long-snowflake-6u13n?file=/src/Test.jsx
我的设计意图是动态生成输入,然后允许用户具有特定于每个输入的样式,这些样式在视觉上有助于根据外部事件分解每个句子。但在我继续前进之前,我的输入容器仅与其中的文本一样大非常重要。
为什么不使用文本区域? -- 我有特定于每个句子的数据,我想为其创建独特的样式。
有什么想法吗?
【问题讨论】:
【参考方案1】:如果字体是等宽字体,则使用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
中,因为它似乎删除了输入字段中的一个小的水平滚动。
这是为了让您以您想要的方式进一步调整,例如当它达到视口宽度时应该如何表现。
【讨论】:
我做了一个简单的 html/CSS/JS 答案,你可以使用任何你认为对你的答案有用的东西。我稍后会删除我的(主要是关于将跨度/输入设置为相同大小的 CSS 部分)。 你为什么要删除你的?这是一种不同的,显然更优雅的方法。 ? 它没有反应精神的编码方式;)【参考方案2】:这是一种来自纯 HTML/CSS 和工作 sn-p 的方法,将在 span
中键入的值隐藏在 input
集合中的 absolute
position
后面。 CSS 可以使span
和input
匹配相同的长度/宽度。拉伸/折叠父级 (label
) 将完成这项工作。
感谢@silvenon,您还可以在 sn-p 下方找到一个反应示例
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,您可以找到该代码的反应示例。
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>
)
【讨论】:
OP 很容易将其翻译为 React,但为了完整起见,如果您想编辑答案:gist.github.com/silvenon/0df75af6abb572c646c722341dcf7356 @silvenon,你真是太好了,希望有一天我会足够熟悉 react 这样做。保持安全。 Np ? 我又添加了一个待审核的编辑,我在声明组件时不小心写了StyledInput
而不是SentenceInput
。事后看来,我本可以在这里指出错误,而不是让其他人批准编辑。 ?
感谢你们!在浏览了很多不同的资源之后,我实际上最终实现了一个非常不同的解决方案。与这里的内容相比,它涉及的内容要多得多,并且使用可能不适合大多数环境的可编辑 div。所以,我认为我不会发布替代解决方案,至少现在还不会。也就是说,你们俩都提供了非常好的解决方案。 :)【参考方案3】:
<input>
size
属性可以用来设置宽度。它的工作方式与 ch 单位相同,例如<input size="10">
的宽度为 10ch
。与ch
单位一样,它与等宽字体完美搭配,但在比例间距字体方面只是一个近似值。
要使用按比例间隔的字体获得更准确的测量结果,您可以使用<input>
元素本身的scrollWidth
属性来计算宽度。诀窍是先将值设置为“自动”以在设置值之前捕获内容宽度。如果您想完全适合内容,请将输入 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.scrollWidthpx`;
);
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;
【讨论】:
以上是关于反应:如何使输入仅与提供的文本量一样宽?的主要内容,如果未能解决你的问题,请参考以下文章