在 React 中自动缩放输入值的宽度

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

我想要一个宽度能够适应其内容的输入。

我正在尝试实现类似问题的答案,但使用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
)。

我怎样才能实现这个目标?

javascript reactjs react-hooks resize
5个回答
29
投票

经过一番折腾,我找到了解决方案!

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。

这是一个工作演示


8
投票

嗯,这很有趣! 我尝试了一些不同的想法,但没有一个能完美地工作——尤其是如果它们是用一种有点受人尊敬的代码编写的。

但是我发现了这篇文章并决定尝试一下。 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


3
投票

这是我找到的最简单的解决方案。

您创建一个将在更改时使用的函数

   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 轴上的“可滚动宽度”并将其设置为宽度。

Nb:归功于这个人:https://www.youtube.com/watch?v=87wfMZ56egU


2
投票

在react中发现了使用Refs的技巧。

style={{ width: inputRef.current ? inputRef.current.value.length + 'ch' : 'auto' }}

并为元素设置

ref={inputRef}
。 请记住在 CSS 中设置输入的最小宽度。


0
投票

不使用额外隐藏元素的另一种选择:

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>

© www.soinside.com 2019 - 2024. All rights reserved.