Como dimensionar automaticamente uma área de texto usando o Prototype?

121

Atualmente, estou trabalhando em um aplicativo de vendas interno para a empresa em que trabalho e tenho um formulário que permite ao usuário alterar o endereço de entrega.

Agora, acho que ficaria muito melhor, se a área de texto que estou usando para os detalhes do endereço principal ocupasse apenas a área do texto e redimensionasse automaticamente se o texto fosse alterado.

Aqui está uma captura de tela dele atualmente.

Endereço ISO

Alguma ideia?


@Chris

Um bom ponto, mas há razões pelas quais quero que seja redimensionado. Quero que a área ocupada seja a área das informações nele contidas. Como você pode ver na captura de tela, se eu tiver uma área de texto fixa, ela ocupa bastante espaço vertical.

Posso reduzir a fonte, mas preciso que o endereço seja grande e legível. Agora posso reduzir o tamanho da área de texto, mas tenho problemas com pessoas que têm uma linha de endereço que usa 3 ou 4 (uma usa 5) linhas. É necessário que o usuário use uma barra de rolagem.

Acho que devo ser um pouco mais específico. Estou procurando o redimensionamento vertical e a largura não importa tanto. O único problema que acontece com isso é o número ISO (o grande "1") é empurrado para o endereço quando a largura da janela é muito pequena (como você pode ver na captura de tela).

Não se trata de ter um truque; trata-se de ter um campo de texto que o usuário pode editar que não ocupará espaço desnecessário, mas mostrará todo o texto nele.

Embora, se alguém criar outra maneira de abordar o problema, eu também esteja aberto a isso.


Eu modifiquei o código um pouco porque estava agindo um pouco estranho. Mudei para ativar no keyup, porque não levaria em consideração o caractere que acabou de ser digitado.

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
fonte
Você pode criar um site de demonstração onde podemos ver isso no trabalho?
Einar Ólafsson
3
este plugin parece bom jacklmoore.com/autosize
Gaurav Shah
Existe uma versão JQuery? Como acessar colunas e linhas de uma TextArea no JQuery?
Zach
Quase o mesmo, mas com requisitos explícitos que devem ficar menores quando o texto é removido: stackoverflow.com/questions/454202/…
Ciro Santilli

Respostas:

75

O Facebook faz isso quando você escreve nas paredes das pessoas, mas apenas redimensiona verticalmente.

O redimensionamento horizontal me parece uma bagunça, devido a quebra de linha, linhas longas e assim por diante, mas o redimensionamento vertical parece ser bastante seguro e agradável.

Nenhum dos novatos que usam o Facebook que conheço já mencionou algo sobre isso ou ficou confuso. Eu usaria isso como evidência anedótica para dizer 'vá em frente, implemente'.

Algum código JavaScript para fazer isso, usando Prototype (porque é com isso que estou 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>

PS: Obviamente, esse código JavaScript é muito ingênuo e não é bem testado, e você provavelmente não deseja usá-lo em caixas de texto com romances, mas você entende a ideia geral.

Orion Edwards
fonte
2
Isso pressupõe que o navegador quebre com qualquer caractere incorreto. Experimente <textarea cols='5'>0 12345 12345 0</textarea>. (Eu escrevi uma implementação quase idêntica e não peguei isso até depois de um mês de uso.) Também falha nas linhas em branco.
st-boost
Existe uma versão JQuery para isso?
Emeraldhieu # 28/15
A barra invertida em $ A (str.split ("\ n")) precisa de outra. (My edição da resposta acima não sobreviveu por algum motivo.)
Gherson
67

Um refinamento para algumas dessas respostas é permitir que o CSS faça mais do trabalho.

A rota básica parece ser:

  1. Crie um elemento de contêiner para armazenar o textarea oculto e ocultodiv
  2. Usando o Javascript, manter os textarea'conteúdos s sincronizados com as div' s
  3. Deixe o navegador fazer o trabalho de calcular a altura dessa div
  4. Como o navegador lida com a renderização / dimensionamento do oculto div, evitamos definir explicitamente a textareaaltura do.

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
fonte
5
Esta é uma solução incrível! A única ressalva é que os navegadores que não suportam o tamanho da caixa (IE <7) não calcularão corretamente a largura e, portanto, você perderá alguma precisão, mas se estiver deixando espaço no final de uma linha, você ainda deveria estar bem. Definitivamente amá-lo pela simplicidade.
Antonio Salazar Cardozo
4
Tomei a liberdade de implementar esta solução como um plugin jQuery autônomo e simples de usar, sem necessidade de marcação ou estilo. xion.io/jQuery.xarea no caso de alguém de poderia usá-lo :)
Xion
2
definindo textCopy para oculto ainda permite que a div oculta para ocupar espaço na marcação assim como o conteúdo textCopy ficar maior, você acabará por ter uma barra de rolagem ao longo de todo o comprimento da sua página ...
topwik
1
Outro belo ajuste para isso; var text = $("#textArea").val().replace(/\n/g, '<br/>') + '&nbsp;';garante que você não tenha um comportamento irregular ao passar de uma nova linha vazia no final da área de texto para uma não-vazia, pois a div oculta assegura a quebra do nbsp.
involuntário
1
@unwitting excelente chamada para adicionar um '& nbsp;' para se livrar desse aumento repentino de tamanho quando você adiciona a primeira letra a uma nova linha - para mim está quebrada sem isso - isso deve ser adicionado à resposta!
Davnicwil 11/07
40

Aqui está outra técnica para dimensionar automaticamente uma área de texto.

  • Usa a altura do pixel em vez da altura da linha: manipulação mais precisa da quebra de linha se uma fonte proporcional for usada.
  • Aceita ID ou elemento como entrada
  • Aceita um parâmetro opcional de altura máxima - útil se você preferir não deixar a área de texto crescer além de um determinado tamanho (mantenha tudo na tela, evite quebrar o layout etc.)
  • Testado no Firefox 3 e Internet Explorer 6

Código: (JavaScript simples baunilha)

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

Demonstração: (usa jQuery, destinos na área de texto em que estou digitando agora - se você possui o Firebug instalado, cole as duas amostras no console e teste nesta página)

$("#post-text").keyup(function()
{
   FitToContent(this, document.documentElement.clientHeight)
});
Shog9
fonte
@SMB - que provavelmente não seria muito difícil de implementar, basta adicionar outro condicional
Jason
@ Jason: Quando o texto na caixa não o preenche clientHeighte scrollHeighté igual, então você não pode usar esse método se quiser que a área de texto diminua e cresça.
Brant Bobby
Para simplificá-lo, basta adicionar este código antes da linha 6:if (text.clientHeight == text.scrollHeight) text.style.height = "20px";
Gapipro 20/10/11
14

Provavelmente a solução mais curta:

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

Dessa forma, você não precisa de nenhum divs oculto ou algo assim.

Nota: talvez você precise brincar, this.style.height = (this.scrollHeight) + "px";dependendo de como estilizar a área de texto (altura da linha, preenchimento e esse tipo de coisa).

Eduard Luca
fonte
A melhor solução se você não se importa de piscar na barra de rolagem.
cfillol
Seria suficiente capturar apenas o evento de keyup. Você não acha?
cfillol
2
Defina a propriedade overflow da área de texto como "oculta" para evitar que a barra de rolagem pisque.
cfillol
keyuppode ser o suficiente, mas não tenho certeza. Fiquei tão feliz que descobri que parei de tentar outra coisa.
Eduard Luca
Ainda estou tentando descobrir por que você adicionou this.style.height = "1px"; ou this.style.height = "auto"; antes. Sei que a área de texto não será redimensionada quando removermos o conteúdo se não adicionarmos esta linha. Alguém pode se importar em explicar?
precisa
8

Aqui está uma versão Prototype de redimensionar uma área de texto que não depende do número de colunas na área de texto. Essa é uma técnica superior, pois permite controlar a área de texto via CSS, além de ter uma área de texto de largura variável. Além disso, esta versão exibe o número de caracteres restantes. Embora não seja solicitado, é um recurso bastante útil e é facilmente removido se não for desejado.

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

Crie o widget chamando new Widget.Textarea('element_id') . As opções padrão podem ser substituídas passando-as como um objeto, por exemplo new Widget.Textarea('element_id', { max_length: 600, min_height: 50}). Se você deseja criá-lo para todas as áreas de texto da página, faça algo como:

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

Aqui está uma solução com JQuery:

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

abcé um teaxtarea.

memical
fonte
5

Verifique o link abaixo: 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
fonte
3

Apenas revisitando isso, eu o deixei um pouco mais arrumado (embora alguém que esteja cheio de protótipo / JavaScript possa sugerir melhorias?).

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

Basta ligar com:

new TextAreaResize('textarea_id_name_here');
Mike
fonte
3

Eu fiz algo bastante fácil. Primeiro, coloquei a TextArea em um DIV. Segundo, chamei a readyfunção para 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)));
});

Simples. É a altura máxima da div depois que ela é renderizada, dividida pela altura de uma TextArea de uma linha.

Eduardo Mass
fonte
2

Eu precisava dessa função para mim, mas nenhum deles daqui funcionou como eu precisava deles.

Então eu usei o código do Orion e mudei.

Eu adicionei em uma altura mínima, para que na destruição não fique muito pequena.

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

Como a resposta de @memical.

No entanto, eu encontrei algumas melhorias. Você pode usar a height()função jQuery . Mas lembre-se dos pixels no topo e no fundo do preenchimento. Caso contrário, sua área de texto aumentará muito 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
fonte
2

Minha solução não usando jQuery (porque às vezes eles não precisam ser a mesma coisa) está abaixo. Embora tenha sido testado apenas no Internet Explorer 7 , a comunidade pode apontar todos os motivos pelos quais isso está errado:

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

Até agora, eu realmente gosto de como está funcionando e não me preocupo com outros navegadores, então provavelmente o aplicarei a todas as minhas á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
}
user1566694
fonte
1

Aqui está uma extensão do widget Prototype que Jeremy postou em 4 de junho:

Ele impede que o usuário digite mais caracteres se você estiver usando limites nas áreas de texto. Ele verifica se há caracteres restantes. Se o usuário copiar texto na área de texto, o texto será cortado no máximo. comprimento:

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

O @memical tinha uma solução incrível para definir a altura da área de texto no carregamento de página com jQuery, mas para o meu aplicativo eu queria poder aumentar a altura da área de texto à medida que o usuário adicionava mais conteúdo. Criei a solução da memical com o seguinte:

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

Não é muito suave, mas também não é um aplicativo voltado para o cliente; portanto, suavidade não importa. (Se isso estivesse voltado para o cliente, eu provavelmente teria usado apenas um plugin jQuery de redimensionamento automático.)

WNRosenberg
fonte
1

Para aqueles que estão codificando para o IE e encontram esse problema. O IE tem um pequeno truque que o torna 100% CSS.

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

Você pode até fornecer um valor para linhas = "n" que o IE ignorará, mas outros navegadores usarão. Eu realmente odeio codificação que implementa hackers do IE, mas este é muito útil. É possível que ele funcione apenas no modo Quirks.

Einstein47
fonte
1

Os usuários do Internet Explorer, Safari, Chrome e Opera precisam se lembrar de definir explicitamente o valor da altura da linha em CSS. Eu faço uma folha de estilo que define as propriedades iniciais para todas as caixas de texto da seguinte maneira.

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

Aqui está uma função que acabei de escrever no jQuery para fazê-lo - você pode portá-lo para o Prototype , mas eles não suportam a "vitalidade" do jQuery, para que os elementos adicionados pelas solicitações do Ajax não respondam.

Essa versão não apenas se expande, mas também se contrai quando a tecla Delete ou Backspace é pressionada.

Esta versão depende do jQuery 1.4.2.

Aproveitar ;)

http://pastebin.com/SUKeBtnx

Uso:

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

ou (qualquer seletor jQuery, por exemplo)

$("textarea").textareacontrol();

Foi testado no Internet Explorer 7 / Internet Explorer 8 , Firefox 3.5 e Chrome. Tudo funciona bem.

Alex
fonte
1

Usando o ASP.NET, basta fazer o seguinte:

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