React:如何使输入的宽度仅与提供的文本量一样宽?

问题描述 投票:0回答:4

足够简单的问题: 我正在尝试创建与提供给它们的文本一样大的输入。

沙箱:https://codesandbox.io/s/long-snowflake-6u13n?file=/src/Test.jsx

我的设计意图是动态生成输入,然后允许用户具有特定于每个输入的样式,这些样式在视觉上有助于根据外部事件分解每个句子。但在我继续前进之前,我的输入容器与其中的文本一样大,这一点非常重要。

为什么不使用文本区域? -- 我有每个句子特有的数据,我想为其创建独特的样式。

有什么想法吗?

css reactjs styled-components
4个回答
6
投票

这是一种来自纯 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>
  )
}

3
投票

<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;

2
投票

如果字体是等宽字体,则可以使用

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
中,因为它似乎删除了输入字段中的小水平滚动。

您可以按照自己想要的方式进一步调整,例如当它达到视口宽度时应该如何表现。


0
投票

使用

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'
        }
     }}
/>
© www.soinside.com 2019 - 2024. All rights reserved.