javascript/react 动态高度文本区域(最大停止)

Posted

技术标签:

【中文标题】javascript/react 动态高度文本区域(最大停止)【英文标题】:javascript/react dynamic height textarea (stop at a max) 【发布时间】:2017-01-17 00:14:38 【问题描述】:

我想要实现的是一个 textarea,它以单行开始,但会增长到 4 行,如果用户继续键入,此时开始滚动。我有一个部分解决方案有点工作,它会增长,然后在达到最大值时停止,但如果你删除文本,它不会像我想要的那样缩小。

这是我目前所拥有的。

export class foo extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      textareaHeight: 38
    ;
  

  handleKeyUp(evt) 
    // Max: 75px Min: 38px
    let newHeight = Math.max(Math.min(evt.target.scrollHeight + 2, 75), 38);
    if (newHeight !== this.state.textareaHeight) 
      this.setState(
        textareaHeight: newHeight
      );
    
  

  render() 
    let textareaStyle =  height: this.state.textareaHeight ;
    return (
      <div>
        <textarea onKeyUp=this.handleKeyUp.bind(this) style=textareaStyle/>
      </div>
    );
  

很明显,当height 设置为更大的值时,scrollHeight 不会缩小。关于如何解决此问题的任何建议,以便在删除文本时它也会缩小?

【问题讨论】:

【参考方案1】:

另一种简单的方法(无需额外的软件包)

export class foo extends React.Component 
  handleKeyDown(e) 
    e.target.style.height = 'inherit';
    e.target.style.height = `$e.target.scrollHeightpx`; 
    // In case you have a limitation
    // e.target.style.height = `$Math.min(e.target.scrollHeight, limit)px`;
  

  render() 
    return <textarea onKeyDown=this.handleKeyDown />;
  

删除文本后textarea没有缩回的问题是因为忘记设置这一行

e.target.style.height = 'inherit';

考虑使用 onKeyDown,因为它适用于所有键,而其他键可能不适用 (w3schools)

如果您有paddingbordertopbottom (reference)

handleKeyDown(e) 
    // Reset field height
    e.target.style.height = 'inherit';

    // Get the computed styles for the element
    const computed = window.getComputedStyle(e.target);

    // Calculate the height
    const height = parseInt(computed.getPropertyValue('border-top-width'), 10)
                 + parseInt(computed.getPropertyValue('padding-top'), 10)
                 + e.target.scrollHeight
                 + parseInt(computed.getPropertyValue('padding-bottom'), 10)
                 + parseInt(computed.getPropertyValue('border-bottom-width'), 10);

    e.target.style.height = `$heightpx`;

我希望这会有所帮助。

【讨论】:

TextArea 有初始长文本时失败【参考方案2】:

你可以使用autosize

LIVE DEMO

import React,  Component  from 'react';
import autosize from 'autosize';

class App extends Component 
    componentDidMount()
       this.textarea.focus();
       autosize(this.textarea);
    
    render()
      const style = 
                maxHeight:'75px',
                minHeight:'38px',
                  resize:'none',
                  padding:'9px',
                  boxSizing:'border-box',
                  fontSize:'15px';
        return (
          <div>Textarea autosize <br/><br/>
            <textarea
            style=style 
            ref=c=>this.textarea=c
            placeholder="type some text"
            rows=1 defaultValue=""/>
          </div>
        );
    

或者如果你更喜欢反应模块https://github.com/andreypopp/react-textarea-autosize

【讨论】:

这很棒。谢谢! 链接失效了。【参考方案3】:

只需使用useEffect 钩子,它将在渲染器期间拾取高度:

import React,  useEffect, useRef, useState from "react";
const defaultStyle = 
    display: "block",
    overflow: "hidden",
    resize: "none",
    width: "100%",
    backgroundColor: "mediumSpringGreen"
;

const AutoHeightTextarea = ( style = defaultStyle, ...etc ) => 
    const textareaRef = useRef(null);
    const [currentValue, setCurrentValue ] = useState("");// you can manage data with it

    useEffect(() => 
        textareaRef.current.style.height = "0px";
        const scrollHeight = textareaRef.current.scrollHeight;
        textareaRef.current.style.height = scrollHeight + "px";
    , [currentValue]);

    return (
        <textarea
            ref=textareaRef
            style=style
            ...etc
            value=currentValue

            onChange=e=>
            setCurrentValue(e.target.value);
            //to do something with value, maybe callback?
            
        />
    );
;

export default AutoHeightTextarea;

【讨论】:

您应该在帖子中添加说明。仅包含代码而没有解释的帖子往往会不受欢迎:) @bikram 同意?【参考方案4】:

你甚至可以使用 react refs 来做到这一点。将 ref 设置为元素

<textarea ref=this.textAreaRef></textarea> // after react 16.3
<textarea ref=textAreaRef=>this.textAreaRef = textAreaRef></textarea> // before react 16.3

并根据需要更新componentDidMountcomponentDidUpdate 的高度。与,

if (this.textAreaRef) this.textAreaRef.style.height = this.textAreaRef.scrollHeight + "px";

【讨论】:

【参考方案5】:

如果你使用钩子“useRef()”真的很简单。

css:

.text-area 
   resize: none;
   overflow: hidden;
   min-height: 30px;

反应组件:

export default () => 
 const textRef = useRef<any>();

 const onChangeHandler = function(e: SyntheticEvent) 
  const target = e.target as htmlTextAreaElement;
  textRef.current.style.height = "30px";
  textRef.current.style.height = `$target.scrollHeightpx`;
 ;

 return (
   <div>
    <textarea
      ref=textRef
      onChange=onChangeHandler
      className="text-area"
     />
    </div>
  );
;

【讨论】:

你不需要 ref 来做到这一点。只有事件才会起作用。 @Pier-AlexandreBouchard 是的,我同意。【参考方案6】:

实际上,您可以通过 useStateuseEffect 摆脱这种情况

function CustomTextarea(minRows) 
  const [rows, setRows] = React.useState(minRows);
  const [value, setValue] = React.useState("");
  
  React.useEffect(() => 
    const rowlen = value.split("\n");

    if (rowlen.length > minRows) 
      setRows(rowlen.length);
    
  , [value]);

  return (
    <textarea rows=rows onChange=(text) => setValue(text.target.value) />
  );

用途

<CustomTextarea minRows=10 />

【讨论】:

【参考方案7】:

我喜欢使用 this.yourRef.current.offsetHeight。由于这是一个文本区域,它不会像&lt;div style=height:"min-content"&gt;this.state.message&lt;/div&gt; 那样响应height:min-content。所以我不使用

uponResize = () => 
 clearTimeout(this.timeout);
  this.timeout = setTimeout(
   this.getHeightOfText.current &&
   this.setState(
    heightOfText: this.getHeightOfText.current.offsetHeight
   ),
  20
 );
;
componentDidMount = () => 
 window.addEventListener('resize', this.uponResize, /*true*/)

componentWillUnmount = () => 
 window.removeEventListener('resize', this.uponResize)

而是使用

componentDidUpdate = () => 
 if(this.state.lastMessage!==this.state.message)
  this.setState(
   lastMessage:this.state.message,
   height:this.yourRef.current.offsetHeight
  )
 

在隐藏的 div 上

<div
 ref=this.yourRef
 style=
  height:this.state.height,
  width:"100%",
  opacity:0,
  zIndex:-1,
  whiteSpace: "pre-line"
 )
>
 this.state.message
</div>

【讨论】:

【参考方案8】:

运行此代码即可...

import React,  useEffect, useRef, useState from "react";
const defaultStyle = 
    display: "block",
    overflow: "hidden",
    resize: "none",
    width: "100%",
    backgroundColor: "mediumSpringGreen"
;

const AutoHeightTextarea = ( style = defaultStyle, ...etc ) => 
    const textareaRef = useRef(null);
    const [currentValue, setCurrentValue ] = useState("");// you can manage data with it

    useEffect(() => 
        textareaRef.current.style.height = "0px";
        const scrollHeight = textareaRef.current.scrollHeight;
        textareaRef.current.style.height = scrollHeight + "px";
    , [currentValue]);

    return (
        <textarea
            ref=textareaRef
            style=style
            ...etc
            value=currentValue

            onChange=e=>
            setCurrentValue(e.target.value);
            //to do something with value, maybe callback?
            
        />
    );
;

export default AutoHeightTextarea;

【讨论】:

以上是关于javascript/react 动态高度文本区域(最大停止)的主要内容,如果未能解决你的问题,请参考以下文章

可以在没有持续回流的情况下动态调整高度的文本区域吗?

spark文本区域高度调整为内容

如何用内容改变文本区域的高度?

如何获得文本区域的高度

jQuery / JS 获取文本区域的滚动条高度

我可以使文本区域与文本一起增长到最大高度吗?