Comment dimensionner automatiquement une zone de texte à l'aide de Prototype?

121

Je travaille actuellement sur une application de vente interne pour l'entreprise pour laquelle je travaille et j'ai un formulaire qui permet à l'utilisateur de modifier l'adresse de livraison.

Maintenant, je pense que cela aurait l'air beaucoup plus agréable si la zone de texte que j'utilise pour les détails de l'adresse principale occupait simplement la zone du texte qu'elle contient et se redimensionnait automatiquement si le texte était modifié.

En voici une capture d'écran actuellement.

Adresse ISO

Des idées?


@Chris

Un bon point, mais il y a des raisons pour lesquelles je veux qu'il soit redimensionné. Je veux que la zone occupée soit la zone des informations qu'elle contient. Comme vous pouvez le voir sur la capture d'écran, si j'ai une zone de texte fixe, cela prend un peu d'espace vertical.

Je peux réduire la police, mais j'ai besoin que l'adresse soit grande et lisible. Maintenant, je peux réduire la taille de la zone de texte, mais j'ai des problèmes avec les personnes qui ont une ligne d'adresse qui prend 3 ou 4 (une prend 5) lignes. Le besoin que l'utilisateur utilise une barre de défilement est un non-non majeur.

Je suppose que je devrais être un peu plus précis. Je suis après un redimensionnement vertical, et la largeur n'a pas autant d'importance. Le seul problème qui se produit avec cela, c'est que le numéro ISO (le grand "1") est poussé sous l'adresse lorsque la largeur de la fenêtre est trop petite (comme vous pouvez le voir sur la capture d'écran).

Il ne s'agit pas d'avoir un gadget; il s'agit d'avoir un champ de texte que l'utilisateur peut modifier qui ne prendra pas d'espace inutile, mais affichera tout le texte qu'il contient.

Mais si quelqu'un trouve une autre façon d'aborder le problème, je suis ouvert à cela aussi.


J'ai un peu modifié le code car il agissait un peu bizarrement. Je l'ai changé pour l'activer au clavier, car il ne prendrait pas en compte le caractère qui venait d'être tapé.

resizeIt = function() {
  var str = $('iso_address').value;
  var cols = $('iso_address').cols;
  var linecount = 0;

  $A(str.split("\n")).each(function(l) {
    linecount += 1 + Math.floor(l.length / cols); // Take into account long lines
  })

  $('iso_address').rows = linecount;
};
Mike
la source
Pouvez-vous créer un site de démonstration où nous pouvons voir cela au travail?
Einar Ólafsson
3
ce plugin semble bon jacklmoore.com/autosize
Gaurav Shah
Existe-t-il une version JQuery? Comment accéder aux cols et aux lignes d'un TextArea dans JQuery?
Zach
Presque la même chose, mais avec une exigence explicite qui devrait devenir plus petite lorsque le texte est supprimé: stackoverflow.com/questions/454202/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

75

Facebook le fait lorsque vous écrivez sur les murs des gens, mais ne se redimensionne que verticalement.

Le redimensionnement horizontal me semble être un désordre, en raison du retour à la ligne, des longues lignes, etc., mais le redimensionnement vertical semble être assez sûr et agréable.

Aucun des nouveaux utilisateurs de Facebook que je connais n'a jamais mentionné quoi que ce soit à ce sujet ou n'a jamais été confus. J'utiliserais cela comme preuve anecdotique pour dire «allez-y, mettez-le en œuvre».

Un peu de code JavaScript pour le faire, en utilisant Prototype (car c'est ce que je connais bien):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <script src="http://www.google.com/jsapi"></script>
        <script language="javascript">
            google.load('prototype', '1.6.0.2');
        </script>
    </head>

    <body>
        <textarea id="text-area" rows="1" cols="50"></textarea>

        <script type="text/javascript" language="javascript">
            resizeIt = function() {
              var str = $('text-area').value;
              var cols = $('text-area').cols;

              var linecount = 0;
              $A(str.split("\n")).each( function(l) {
                  linecount += Math.ceil( l.length / cols ); // Take into account long lines
              })
              $('text-area').rows = linecount + 1;
            };

            // You could attach to keyUp, etc. if keydown doesn't work
            Event.observe('text-area', 'keydown', resizeIt );

            resizeIt(); //Initial on load
        </script>
    </body>
</html>

PS: De toute évidence, ce code JavaScript est très naïf et pas bien testé, et vous ne voulez probablement pas l'utiliser sur des zones de texte contenant des romans, mais vous avez l'idée générale.

Orion Edwards
la source
2
Cela suppose que le navigateur s'arrête à n'importe quel caractère, ce qui est incorrect. Essayez-le <textarea cols='5'>0 12345 12345 0</textarea>. (J'ai écrit une implémentation presque identique et je n'ai compris cela qu'après un mois d'utilisation.) Cela échoue également pour les lignes vides.
st-boost
Existe-t-il une version JQuery pour cela?
emeraldhieu
La barre oblique inverse dans $ A (str.split ("\ n")) en a besoin d'une autre. (Ma modification de la réponse ci-dessus n'a pas survécu pour une raison quelconque.)
gherson
67

Une amélioration de certaines de ces réponses est de laisser CSS faire plus de travail.

L'itinéraire de base semble être:

  1. Créez un élément conteneur pour contenir le textareaet undiv
  2. En utilisant Javascript, gardez le textareacontenu du 's synchronisé avec le div' s
  3. Laissez le navigateur faire le travail de calcul de la hauteur de ce div
  4. Parce que le navigateur gère le rendu / dimensionnement du caché div, nous évitons de définir explicitement la textareahauteur du.

document.addEventListener('DOMContentLoaded', () => {
    textArea.addEventListener('change', autosize, false)
    textArea.addEventListener('keydown', autosize, false)
    textArea.addEventListener('keyup', autosize, false)
    autosize()
}, false)

function autosize() {
    // Copy textarea contents to div browser will calculate correct height
    // of copy, which will make overall container taller, which will make
    // textarea taller.
    textCopy.innerHTML = textArea.value.replace(/\n/g, '<br/>')
}
html, body, textarea {
    font-family: sans-serif;
    font-size: 14px;
}

.textarea-container {
    position: relative;
}

.textarea-container > div, .textarea-container > textarea {
    word-wrap: break-word; /* make sure the div and the textarea wrap words in the same way */
    box-sizing: border-box;
    padding: 2px;
    width: 100%;
}

.textarea-container > textarea {
    overflow: hidden;
    position: absolute;
    height: 100%;
}

.textarea-container > div {
    padding-bottom: 1.5em; /* A bit more than one additional line of text. */ 
    visibility: hidden;
}
<div class="textarea-container">
    <textarea id="textArea"></textarea>
    <div id="textCopy"></div>
</div>

Jan Miksovsky
la source
5
C'est une solution géniale! La seule mise en garde est que les navigateurs qui ne prennent pas en charge le dimensionnement de la boîte (IE <7) ne calculeront pas correctement la largeur et vous perdrez donc une certaine précision, mais si vous laissez un espace d'une ligne à la fin, vous devriez toujours aller bien. Je l'aime vraiment pour sa simplicité.
Antonio Salazar Cardozo
4
J'ai pris la liberté d'implémenter cette solution en tant que plugin jQuery autonome et simple à utiliser, sans balisage ni style requis. xion.io/jQuery.xarea au cas où quelqu'un pourrait l'utiliser :)
Xion
2
définir textCopy sur hidden permet toujours au div caché de prendre de l'espace dans le balisage afin que le contenu de textCopy s'agrandisse, vous obtiendrez finalement une barre de défilement sur toute la longueur de votre page ...
topwik
1
Un autre bon ajustement à ceci; var text = $("#textArea").val().replace(/\n/g, '<br/>') + '&nbsp;';s'assure que vous n'obtenez pas de comportement saccadé lorsque vous passez d'une nouvelle ligne vide à la fin de la zone de texte à une autre non vide, car la div cachée s'assure de s'enrouler dans le nbsp.
involontaire
1
@unwitting grand appel sur l'ajout d'un '& nbsp;' pour se débarrasser de cette augmentation soudaine de taille lorsque vous ajoutez la première lettre à une nouvelle ligne - pour moi, c'est cassé sans cela - cela devrait être ajouté à la réponse!
davnicwil
40

Voici une autre technique pour dimensionner automatiquement une zone de texte.

  • Utilise la hauteur des pixels au lieu de la hauteur de la ligne: une gestion plus précise du retour à la ligne si une police proportionnelle est utilisée.
  • Accepte l'ID ou l'élément comme entrée
  • Accepte un paramètre facultatif de hauteur maximale - utile si vous préférez ne pas laisser la zone de texte dépasser une certaine taille (tout conserver à l'écran, éviter de casser la mise en page, etc.)
  • Testé sur Firefox 3 et Internet Explorer 6

Code: (JavaScript vanille ordinaire)

function FitToContent(id, maxHeight)
{
   var text = id && id.style ? id : document.getElementById(id);
   if (!text)
      return;

   /* Accounts for rows being deleted, pixel value may need adjusting */
   if (text.clientHeight == text.scrollHeight) {
      text.style.height = "30px";
   }

   var adjustedHeight = text.clientHeight;
   if (!maxHeight || maxHeight > adjustedHeight)
   {
      adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
      if (maxHeight)
         adjustedHeight = Math.min(maxHeight, adjustedHeight);
      if (adjustedHeight > text.clientHeight)
         text.style.height = adjustedHeight + "px";
   }
}

Démo: (utilise jQuery, des cibles sur la zone de texte dans laquelle je tape en ce moment - si Firebug est installé, collez les deux exemples dans la console et testez sur cette page)

$("#post-text").keyup(function()
{
   FitToContent(this, document.documentElement.clientHeight)
});
Shog9
la source
@SMB - ce ne serait probablement pas trop difficile à mettre en œuvre, ajoutez simplement un autre conditionnel
Jason
@Jason: Lorsque le texte de la boîte ne le remplit pas clientHeightet scrollHeightest égal, vous ne pouvez donc pas utiliser cette méthode si vous voulez que votre zone de texte se rétrécisse et s'agrandisse.
Brant Bobby
Pour simplement le réduire, il vous suffit d'ajouter ce code avant la ligne 6:if (text.clientHeight == text.scrollHeight) text.style.height = "20px";
Gapipro
14

Probablement la solution la plus courte:

jQuery(document).ready(function(){
    jQuery("#textArea").on("keydown keyup", function(){
        this.style.height = "1px";
        this.style.height = (this.scrollHeight) + "px"; 
    });
});

De cette façon, vous n'avez pas besoin de div cachés ou de quelque chose comme ça.

Remarque: vous devrez peut-être jouer avec en this.style.height = (this.scrollHeight) + "px";fonction de la façon dont vous stylisez la zone de texte (hauteur de ligne, remplissage et ce genre de choses).

Eduard Luca
la source
La meilleure solution si cela ne vous dérange pas de clignoter la barre de défilement.
cfillol
Il suffirait de capturer uniquement l'événement keyup. Tu ne penses pas?
cfillol
2
Définissez la propriété de débordement de textarea sur "hidden" pour éviter que la barre de défilement ne clignote.
cfillol
keyuppourrait être suffisant, mais je ne suis pas sûr. J'étais si heureux de l'avoir compris, que j'ai arrêté d'essayer autre chose.
Eduard Luca
J'essaie toujours de savoir pourquoi vous avez ajouté this.style.height = "1px"; ou this.style.height = "auto"; avant. Je sais que textarea ne sera pas redimensionné lorsque nous supprimons le contenu si nous n'ajoutons pas cette ligne. Quelqu'un peut-il expliquer?
Balasubramani M
8

Voici une version prototype du redimensionnement d'une zone de texte qui ne dépend pas du nombre de colonnes dans la zone de texte. C'est une technique supérieure car elle vous permet de contrôler la zone de texte via CSS ainsi que d'avoir une zone de texte à largeur variable. De plus, cette version affiche le nombre de caractères restants. Bien que cela ne soit pas demandé, c'est une fonctionnalité très utile et est facilement supprimée si vous ne le souhaitez pas.

//inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
if (window.Widget == undefined) window.Widget = {}; 

Widget.Textarea = Class.create({
  initialize: function(textarea, options)
  {
    this.textarea = $(textarea);
    this.options = $H({
      'min_height' : 30,
      'max_length' : 400
    }).update(options);

    this.textarea.observe('keyup', this.refresh.bind(this));

    this._shadow = new Element('div').setStyle({
      lineHeight : this.textarea.getStyle('lineHeight'),
      fontSize : this.textarea.getStyle('fontSize'),
      fontFamily : this.textarea.getStyle('fontFamily'),
      position : 'absolute',
      top: '-10000px',
      left: '-10000px',
      width: this.textarea.getWidth() + 'px'
    });
    this.textarea.insert({ after: this._shadow });

    this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
    this.textarea.insert({after: this._remainingCharacters});  
    this.refresh();  
  },

  refresh: function()
  { 
    this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
    this.textarea.setStyle({
      height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
    });

    var remaining = this.options.get('max_length') - $F(this.textarea).length;
    this._remainingCharacters.update(Math.abs(remaining)  + ' characters ' + (remaining > 0 ? 'remaining' : 'over the limit'));
  }
});

Créez le widget en appelant new Widget.Textarea('element_id'). Les options par défaut peuvent être remplacées en les passant en tant qu'objet, par exemple new Widget.Textarea('element_id', { max_length: 600, min_height: 50}). Si vous souhaitez le créer pour toutes les zones de texte de la page, procédez comme suit:

Event.observe(window, 'load', function() {
  $$('textarea').each(function(textarea) {
    new Widget.Textarea(textarea);
  });   
});
Jeremy Kauffman
la source
7

Voici une solution avec JQuery:

$(document).ready(function() {
    var $abc = $("#abc");
    $abc.css("height", $abc.attr("scrollHeight"));
})

abcest un teaxtarea.

mémique
la source
5

Vérifiez le lien ci-dessous: http://james.padolsey.com/javascript/jquery-plugin-autoresize/

$(document).ready(function () {
    $('.ExpandableTextCSS').autoResize({
        // On resize:
        onResize: function () {
            $(this).css({ opacity: 0.8 });
        },
        // After resize:
        animateCallback: function () {
            $(this).css({ opacity: 1 });
        },
        // Quite slow animation:
        animateDuration: 300,
        // More extra space:
        extraSpace:20,
        //Textarea height limit
        limit:10
    });
});
Gyan
la source
3

En revisitant simplement cela, je l'ai rendu un peu plus net (bien que quelqu'un qui est plein de bouteille sur Prototype / JavaScript puisse suggérer des améliorations?).

var TextAreaResize = Class.create();
TextAreaResize.prototype = {
  initialize: function(element, options) {
    element = $(element);
    this.element = element;

    this.options = Object.extend(
      {},
      options || {});

    Event.observe(this.element, 'keyup',
      this.onKeyUp.bindAsEventListener(this));
    this.onKeyUp();
  },

  onKeyUp: function() {
    // We need this variable because "this" changes in the scope of the
    // function below.
    var cols = this.element.cols;

    var linecount = 0;
    $A(this.element.value.split("\n")).each(function(l) {
      // We take long lines into account via the cols divide.
      linecount += 1 + Math.floor(l.length / cols);
    })

    this.element.rows = linecount;
  }
}

Il suffit d'appeler avec:

new TextAreaResize('textarea_id_name_here');
Mike
la source
3

J'ai fait quelque chose d'assez simple. J'ai d'abord mis le TextArea dans un DIV. Deuxièmement, j'ai appelé la readyfonction de ce script.

<div id="divTable">
  <textarea ID="txt" Rows="1" TextMode="MultiLine" />
</div>

$(document).ready(function () {
  var heightTextArea = $('#txt').height();
  var divTable = document.getElementById('divTable');
  $('#txt').attr('rows', parseInt(parseInt(divTable .style.height) / parseInt(altoFila)));
});

Facile. C'est la hauteur maximale du div une fois qu'il est rendu, divisée par la hauteur d'un TextArea d'une ligne.

Messe Eduardo
la source
2

J'avais besoin de cette fonction pour moi-même, mais aucune de celles d'ici ne fonctionnait comme j'en avais besoin.

J'ai donc utilisé le code d'Orion et l'ai changé.

J'ai ajouté une hauteur minimale pour que la destruction ne devienne pas trop petite.

function resizeIt( id, maxHeight, minHeight ) {
    var text = id && id.style ? id : document.getElementById(id);
    var str = text.value;
    var cols = text.cols;
    var linecount = 0;
    var arStr = str.split( "\n" );
    $(arStr).each(function(s) {
        linecount = linecount + 1 + Math.floor(arStr[s].length / cols); // take into account long lines
    });
    linecount++;
    linecount = Math.max(minHeight, linecount);
    linecount = Math.min(maxHeight, linecount);
    text.rows = linecount;
};
Peter Mortensen
la source
2

Comme la réponse de @memical.

Cependant j'ai trouvé quelques améliorations. Vous pouvez utiliser la height()fonction jQuery . Mais soyez conscient des pixels de remplissage en haut et en bas. Sinon, votre zone de texte se développera trop vite.

$(document).ready(function() {
  $textarea = $("#my-textarea");

  // There is some diff between scrollheight and height:
  //    padding-top and padding-bottom
  var diff = $textarea.prop("scrollHeight") - $textarea.height();
  $textarea.live("keyup", function() {
    var height = $textarea.prop("scrollHeight") - diff;
    $textarea.height(height);
  });
});
Anatoly Mironov
la source
2

Ma solution n'utilisant pas jQuery (car parfois ils ne doivent pas être la même chose) est ci-dessous. Bien qu'il n'ait été testé que dans Internet Explorer 7 , la communauté peut donc indiquer toutes les raisons pour lesquelles cela est faux:

textarea.onkeyup = function () { this.style.height = this.scrollHeight + 'px'; }

Jusqu'à présent, j'aime vraiment son fonctionnement, et je me fiche des autres navigateurs, donc je vais probablement l'appliquer à toutes mes zones de texte:

// Make all textareas auto-resize vertically
var textareas = document.getElementsByTagName('textarea');

for (i = 0; i<textareas.length; i++)
{
    // Retain textarea's starting height as its minimum height
    textareas[i].minHeight = textareas[i].offsetHeight;

    textareas[i].onkeyup = function () {
        this.style.height = Math.max(this.scrollHeight, this.minHeight) + 'px';
    }
    textareas[i].onkeyup(); // Trigger once to set initial height
}
user1566694
la source
1

Voici une extension du widget Prototype que Jeremy a posté le 4 juin:

Cela empêche l'utilisateur de saisir plus de caractères si vous utilisez des limites dans les zones de texte. Il vérifie s'il reste des caractères. Si l'utilisateur copie du texte dans la zone de texte, le texte est coupé au maximum. longueur:

/**
 * Prototype Widget: Textarea
 * Automatically resizes a textarea and displays the number of remaining chars
 * 
 * From: http://stackoverflow.com/questions/7477/autosizing-textarea
 * Inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
 */
if (window.Widget == undefined) window.Widget = {}; 

Widget.Textarea = Class.create({
  initialize: function(textarea, options){
    this.textarea = $(textarea);
    this.options = $H({
      'min_height' : 30,
      'max_length' : 400
    }).update(options);

    this.textarea.observe('keyup', this.refresh.bind(this));

    this._shadow = new Element('div').setStyle({
      lineHeight : this.textarea.getStyle('lineHeight'),
      fontSize : this.textarea.getStyle('fontSize'),
      fontFamily : this.textarea.getStyle('fontFamily'),
      position : 'absolute',
      top: '-10000px',
      left: '-10000px',
      width: this.textarea.getWidth() + 'px'
    });
    this.textarea.insert({ after: this._shadow });

    this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
    this.textarea.insert({after: this._remainingCharacters});  
    this.refresh();  
  },

  refresh: function(){ 
    this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
    this.textarea.setStyle({
      height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
    });

    // Keep the text/character count inside the limits:
    if($F(this.textarea).length > this.options.get('max_length')){
      text = $F(this.textarea).substring(0, this.options.get('max_length'));
        this.textarea.value = text;
        return false;
    }

    var remaining = this.options.get('max_length') - $F(this.textarea).length;
    this._remainingCharacters.update(Math.abs(remaining)  + ' characters remaining'));
  }
});
singe lorem
la source
1

@memical avait une solution géniale pour définir la hauteur de la zone de texte sur pageload avec jQuery, mais pour mon application, je voulais pouvoir augmenter la hauteur de la zone de texte à mesure que l'utilisateur ajoutait plus de contenu. J'ai construit la solution de memical avec ce qui suit:

$(document).ready(function() {
    var $textarea = $("p.body textarea");
    $textarea.css("height", ($textarea.attr("scrollHeight") + 20));
    $textarea.keyup(function(){
        var current_height = $textarea.css("height").replace("px", "")*1;
        if (current_height + 5 <= $textarea.attr("scrollHeight")) {
            $textarea.css("height", ($textarea.attr("scrollHeight") + 20));
        }
    });
});

Ce n'est pas très fluide mais ce n'est pas non plus une application orientée client, donc la fluidité n'a pas vraiment d'importance. (Si cela avait été fait face au client, j'aurais probablement juste utilisé un plugin jQuery de redimensionnement automatique.)

WNRosenberg
la source
1

Pour ceux qui codent pour IE et rencontrent ce problème. IE a un petit truc qui le rend 100% CSS.

<TEXTAREA style="overflow: visible;" cols="100" ....></TEXTAREA>

Vous pouvez même fournir une valeur pour rows = "n" que IE ignorera, mais que d'autres navigateurs utiliseront. Je déteste vraiment le codage qui implémente des hacks IE, mais celui-ci est très utile. Il est possible que cela ne fonctionne qu'en mode Quirks.

Einstein47
la source
1

Les utilisateurs d'Internet Explorer, Safari, Chrome et Opera doivent se souvenir de définir explicitement la valeur de hauteur de ligne dans CSS. Je fais une feuille de style qui définit les propriétés initiales de toutes les zones de texte comme suit.

<style>
    TEXTAREA { line-height: 14px; font-size: 12px; font-family: arial }
</style>
Larry
la source
1

Voici une fonction que je viens d'écrire dans jQuery pour le faire - vous pouvez la porter sur Prototype , mais ils ne supportent pas la "vivacité" de jQuery donc les éléments ajoutés par les requêtes Ajax ne répondront pas.

Cette version se développe non seulement, mais elle se contracte également lorsque vous appuyez sur la touche de suppression ou de retour arrière.

Cette version repose sur jQuery 1.4.2.

Prendre plaisir ;)

http://pastebin.com/SUKeBtnx

Usage:

$("#sometextarea").textareacontrol();

ou (n'importe quel sélecteur jQuery par exemple)

$("textarea").textareacontrol();

Il a été testé sur Internet Explorer 7 / Internet Explorer 8 , Firefox 3.5 et Chrome. Tout fonctionne bien.

Alex
la source
1

En utilisant ASP.NET, faites simplement ceci:

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Automatic Resize TextBox</title>
        <script type="text/javascript">
            function setHeight(txtarea) {
                txtarea.style.height = txtdesc.scrollHeight + "px";
            }
        </script>
    </head>

    <body>
        <form id="form1" runat="server">
            <asp:TextBox ID="txtarea" runat= "server" TextMode="MultiLine"  onkeyup="setHeight(this);" onkeydown="setHeight(this);" />
        </form>
    </body>
</html>
Pat Murray
la source