web-dev-qa-db-fra.com

javascript / react zone de hauteur dynamique (arrêtez-vous au maximum)

Ce que j'essaie de réaliser, c'est une zone de texte qui commence comme une seule ligne mais qui augmentera jusqu'à 4 lignes et à ce moment-là commencera à défiler si l'utilisateur continue de taper. J'ai une solution partielle qui fonctionne, elle grandit puis s'arrête lorsqu'elle atteint le maximum, mais si vous supprimez du texte, il ne rétrécit pas comme je le souhaite.

C'est ce que j'ai jusqu'à présent.

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

De toute évidence, le problème est que scrollHeight ne diminue pas lorsque height est défini sur quelque chose de plus grand. Une suggestion sur la façon dont je pourrais résoudre ce problème afin qu'il diminue également si le texte est supprimé?

10
jcmitch

vous pouvez utiliser taille automatique pour cela

DÉMO EN DIRECT

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

ou si vous préférez les modules react https://github.com/andreypopp/react-textarea-autosize

16
Kokovin Vladislav

UNE AUTRE APPROCHE SIMPLE (sans package supplémentaire)

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

Le problème lorsque vous supprimez le texte et la zone de texte ne rétrécit pas est parce que vous oubliez de définir cette ligne

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

Pensez à utiliser onKeyDown car il fonctionne pour toutes les clés alors que d'autres ne le peuvent pas ( w3schools )

Si vous avez padding ou border de top ou bottom. ( référence )

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

J'espère que cela peut aider.

10
Tatsu

vous pouvez même le faire avec des références de réaction. comme définition ref à l'élément

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

et mettez à jour la hauteur sur componentDidMount ou componentDidUpdate selon vos besoins. avec,

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

Vraiment simple si vous utilisez des hooks "useRef ()".

css:

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

composant réactif:

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>
  );
};
1
sajan

Utilisez simplement le crochet useEffect qui récupérera la hauteur pendant le rendu:

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;