¿Cómo auto dimensionar un área de texto usando Prototype?

121

Actualmente estoy trabajando en una aplicación de ventas internas para la empresa para la que trabajo y tengo un formulario que permite al usuario cambiar la dirección de entrega.

Ahora creo que se vería mucho mejor, si el área de texto que estoy usando para los detalles de la dirección principal solo ocupara el área del texto y automáticamente cambiara de tamaño si se cambiara el texto.

Aquí hay una captura de pantalla actualmente.

Dirección ISO

¿Algunas ideas?


@Chris

Un buen punto, pero hay razones por las que quiero que cambie de tamaño. Quiero que el área que ocupa sea el área de la información que contiene. Como puede ver en la captura de pantalla, si tengo un área de texto fija, ocupa bastante espacio vertical.

Puedo reducir la fuente, pero necesito que la dirección sea grande y legible. Ahora puedo reducir el tamaño del área de texto, pero luego tengo problemas con las personas que tienen una línea de dirección que toma 3 o 4 (una toma 5) líneas. Necesitar que el usuario use una barra de desplazamiento es un gran no-no.

Supongo que debería ser un poco más específico. Estoy tras el cambio de tamaño vertical, y el ancho no importa tanto. El único problema que ocurre con eso es que el número ISO (el "1" grande) se coloca debajo de la dirección cuando el ancho de la ventana es demasiado pequeño (como puede ver en la captura de pantalla).

No se trata de tener un truco; se trata de tener un campo de texto que el usuario pueda editar y que no ocupe espacio innecesario, pero que muestre todo el texto que contiene.

Aunque si a alguien se le ocurre otra forma de abordar el problema, yo también estoy abierto a eso.


Modifiqué un poco el código porque estaba actuando un poco extraño. Lo cambié para que se active al presionar la tecla, porque no tomaría en consideración el carácter que se acaba de escribir.

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;
};
Miguel
fuente
¿Puede crear un sitio de demostración donde podamos ver esto en funcionamiento?
Einar Ólafsson
3
este complemento parece bueno jacklmoore.com/autosize
Gaurav Shah
¿Existe una versión de JQuery? ¿Cómo acceder a columnas y filas de un TextArea en JQuery?
Zach
Casi lo mismo, pero con el requisito explícito de que debería
reducirse

Respuestas:

75

Facebook lo hace cuando escribes en las paredes de las personas, pero solo cambia de tamaño verticalmente.

El cambio de tamaño horizontal me parece un desastre, debido al ajuste de palabras, las líneas largas, etc., pero el cambio de tamaño vertical parece ser bastante seguro y agradable.

Ninguno de los novatos que usan Facebook que conozco ha mencionado algo al respecto o se ha sentido confundido. Usaría esto como evidencia anecdótica para decir 'adelante, impleméntalo'.

Algún código JavaScript para hacerlo, usando Prototype (porque eso es con lo que estoy familiarizado):

<!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>

PD: Obviamente, este código JavaScript es muy ingenuo y no está bien probado, y probablemente no quieras usarlo en cuadros de texto con novelas, pero entiendes la idea general.

Orion Edwards
fuente
2
Esto supone que el navegador se rompe en cualquier carácter, lo cual es incorrecto. Pruébelo <textarea cols='5'>0 12345 12345 0</textarea>. (Escribí una implementación casi idéntica y no capté esto hasta después de un mes de usarla). También falla para las líneas en blanco.
st-boost
¿Existe una versión de JQuery para esto?
emeraldhieu
La barra invertida en $ A (str.split ("\ n")) necesita otra. (Mi edición de la respuesta anterior no sobrevivió por alguna razón.)
gherson
67

Un refinamiento de algunas de estas respuestas es dejar que CSS haga más trabajo.

La ruta básica parece ser:

  1. Cree un elemento contenedor para contener el textareay un ocultodiv
  2. Usando Javascript, mantenga el textareacontenido de 'sincronizado con el div' s
  3. Deje que el navegador haga el trabajo de calcular la altura de ese div
  4. Debido a que el navegador maneja la representación / tamaño de lo oculto div, evitamos establecer explícitamente la textareaaltura de la.

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
fuente
5
¡Esta es una solución increíble! La única advertencia es que los navegadores que no admiten el tamaño de caja (IE <7) no calcularán correctamente el ancho y, por lo tanto, perderá algo de precisión, pero si deja el espacio de una línea al final, todavía deberías estar bien. Definitivamente me encanta por la simplicidad.
Antonio Salazar Cardozo
4
Me tomé la libertad de implementar esta solución como complemento jQuery independiente y fácil de usar, sin necesidad de marcado o estilo. xion.io/jQuery.xarea en caso de que alguien pueda usarlo :)
Xion
2
configurar textCopy en hidden todavía permite que el div oculto ocupe espacio en el marcado, de modo que a medida que el contenido de textCopy se agranda, eventualmente obtendrá una barra de desplazamiento a lo largo de toda la página ...
topwik
1
Otro buen ajuste a esto; var text = $("#textArea").val().replace(/\n/g, '<br/>') + '&nbsp;';se asegura de que no tenga un comportamiento desigual al pasar de una nueva línea vacía al final del área de texto a una no vacía, ya que el div oculto se asegura de ajustarse al nbsp.
sin saberlo
1
@inconsciente gran llamada sobre la adición de un '& nbsp;' para deshacerse de ese aumento repentino de tamaño cuando agrega la primera letra a una nueva línea, para mí está rota sin esto, ¡esto debería agregarse a la respuesta!
davnicwil
40

Aquí hay otra técnica para ajustar automáticamente el tamaño de un área de texto.

  • Utiliza la altura de píxeles en lugar de la altura de la línea: manejo más preciso del ajuste de línea si se utiliza una fuente proporcional.
  • Acepta ID o elemento como entrada
  • Acepta un parámetro de altura máxima opcional, útil si prefiere no dejar que el área de texto crezca más allá de un cierto tamaño (mantenerlo todo en pantalla, evitar romper el diseño, etc.)
  • Probado en Firefox 3 e Internet Explorer 6

Código: (JavaScript simple de vainilla)

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

Demostración: (usa jQuery, se dirige al área de texto en la que estoy escribiendo en este momento; si tiene Firebug instalado, pegue ambas muestras en la consola y pruebe en esta página)

$("#post-text").keyup(function()
{
   FitToContent(this, document.documentElement.clientHeight)
});
Shog9
fuente
@SMB: eso probablemente no sería demasiado difícil de implementar, solo agregue otro condicional
Jason
@Jason: cuando el texto en el cuadro no lo llena clientHeighty scrollHeightes igual, no puede usar este método si desea que su área de texto se reduzca y crezca.
Brant Bobby
Para simplemente reducirlo, solo necesita agregar este código antes de la línea 6:if (text.clientHeight == text.scrollHeight) text.style.height = "20px";
Gapipro
14

Probablemente la solución más corta:

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

De esta manera, no necesitas divs ocultos ni nada de eso.

Nota: es posible que tenga que jugar this.style.height = (this.scrollHeight) + "px";dependiendo de cómo diseñe el área de texto (altura de línea, relleno y ese tipo de cosas).

Eduard Luca
fuente
La mejor solución si no le importa que la barra de desplazamiento parpadee.
cfillol
Sería suficiente con capturar solo el evento keyup. ¿No te parece?
cfillol
2
Establezca la propiedad de desbordamiento del área de texto en "oculta" para evitar que la barra de desplazamiento parpadee.
cfillol
keyuppodría ser suficiente, pero no estoy seguro. Estaba tan feliz de haberlo descubierto, que dejé de intentar cualquier otra cosa.
Eduard Luca
Todavía estoy tratando de saber por qué agregaste this.style.height = "1px"; o this.style.height = "auto"; antes de. Sé que textarea no cambiará de tamaño cuando eliminemos el contenido si no agregamos esta línea. ¿Alguien puede querer explicar?
Balasubramani M
8

Aquí hay una versión de prototipo de cambiar el tamaño de un área de texto que no depende del número de columnas en el área de texto. Esta es una técnica superior porque le permite controlar el área de texto a través de CSS, así como tener un área de texto de ancho variable. Además, esta versión muestra el número de caracteres restantes. Si bien no se solicita, es una característica bastante útil y se elimina fácilmente si no se desea.

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

Crea el widget llamando new Widget.Textarea('element_id'). Las opciones predeterminadas se pueden anular pasándolas como un objeto, por ejemplo new Widget.Textarea('element_id', { max_length: 600, min_height: 50}). Si desea crearlo para todas las áreas de texto de la página, haga algo como:

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

Aquí hay una solución con JQuery:

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

abces un teaxtarea.

memical
fuente
5

Consulte el siguiente enlace: 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
fuente
Enlace muerto. El código está aquí james.padolsey.com/demos/plugins/jQuery/autoresize.jquery.js/…
Matthew Hui
3

Solo revisando esto, lo he hecho un poco más ordenado (¿aunque alguien que esté lleno de Prototype / JavaScript podría sugerir mejoras?).

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

Solo llama con:

new TextAreaResize('textarea_id_name_here');
Miguel
fuente
3

Hice algo bastante fácil. Primero puse TextArea en un DIV. En segundo lugar, he llamado a la readyfunción de este 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)));
});

Sencillo. Es la altura máxima del div una vez que se renderiza, dividida por la altura de un TextArea de una fila.

Eduardo Mass
fuente
2

Necesitaba esta función para mí, pero ninguna de las de aquí funcionó como las necesitaba.

Entonces usé el código de Orion y lo cambié.

Agregué una altura mínima, para que en la destrucción no se vuelva demasiado pequeña.

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
fuente
2

Como la respuesta de @memical.

Sin embargo, encontré algunas mejoras. Puede utilizar la height()función jQuery . Pero tenga en cuenta los píxeles de la parte superior y la parte inferior del relleno. De lo contrario, su área de texto crecerá demasiado rápido.

$(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
fuente
2

Mi solución que no usa jQuery (porque a veces no tienen que ser lo mismo) está a continuación. Aunque solo se probó en Internet Explorer 7 , la comunidad puede señalar todas las razones por las que esto es incorrecto:

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

Hasta ahora me gusta mucho cómo funciona, y no me importan otros navegadores, así que probablemente lo aplicaré a todas mis áreas de texto:

// 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
}
usuario1566694
fuente
1

Aquí hay una extensión del widget Prototype que Jeremy publicó el 4 de junio:

Evita que el usuario ingrese más caracteres si está usando límites en áreas de texto. Comprueba si quedan caracteres. Si el usuario copia texto en el área de texto, el texto se corta al máximo. longitud:

/**
 * 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'));
  }
});
mono lorem
fuente
1

@memical tenía una solución increíble para establecer la altura del área de texto en la carga de página con jQuery, pero para mi aplicación quería poder aumentar la altura del área de texto a medida que el usuario agregaba más contenido. Construí la solución de memical con lo siguiente:

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

No es muy suave, pero tampoco es una aplicación orientada al cliente, por lo que la suavidad realmente no importa. (Si esto hubiera sido orientado al cliente, probablemente habría usado un complemento jQuery de cambio de tamaño automático).

WNRosenberg
fuente
1

Para aquellos que están codificando para IE y encuentran este problema. IE tiene un pequeño truco que lo convierte en 100% CSS.

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

Incluso puede proporcionar un valor para las filas = "n" que IE ignorará, pero lo utilizarán otros navegadores. Realmente odio la codificación que implementa hacks de IE, pero este es muy útil. Es posible que solo funcione en modo Quirks.

Einstein47
fuente
1

Los usuarios de Internet Explorer, Safari, Chrome y Opera deben recordar establecer explícitamente el valor de altura de línea en CSS. Hago una hoja de estilo que establece las propiedades iniciales para todos los cuadros de texto de la siguiente manera.

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

Aquí hay una función que acabo de escribir en jQuery para hacerlo: puede portarla a Prototype , pero no admiten la "viveza" de jQuery, por lo que los elementos agregados por las solicitudes de Ajax no responderán.

Esta versión no solo se expande, sino que también se contrae cuando se presiona borrar o retroceder.

Esta versión se basa en jQuery 1.4.2.

Disfruta;)

http://pastebin.com/SUKeBtnx

Uso:

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

o (cualquier selector de jQuery, por ejemplo)

$("textarea").textareacontrol();

Se probó en Internet Explorer 7 / Internet Explorer 8 , Firefox 3.5 y Chrome. Todo funciona bien.

Alex
fuente
1

Usando ASP.NET, simplemente haga esto:

<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
fuente