【问题标题】:javascript/react dynamic height textarea (stop at a max)javascript/react 动态高度文本区域(最大停止)
【发布时间】: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 不会缩小。关于如何解决此问题的任何建议,以便在删除文本时它也会缩小?

【问题讨论】:

    标签: javascript html css reactjs


    【解决方案1】:

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

    export class foo extends React.Component {
      handleKeyDown(e) {
        e.target.style.height = 'inherit';
        e.target.style.height = `${e.target.scrollHeight}px`; 
        // 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 = `${height}px`;
    }
    

    我希望这会有所帮助。

    【讨论】:

    • 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.scrollHeight}px`;
       };
      
       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;

          【讨论】:

            猜你喜欢
            • 2016-06-21
            • 1970-01-01
            • 2022-06-11
            • 1970-01-01
            • 2021-06-19
            • 2015-03-12
            • 1970-01-01
            • 1970-01-01
            • 2014-11-22
            相关资源
            最近更新 更多