Como preservo as quebras de linha ao obter texto de uma textarea?

98

Estou recebendo o valor em uma textarea quando o usuário clica em enviar. Em seguida, pego esse valor e o coloco em outro lugar na página. No entanto, quando faço isso, ele perde caracteres de nova linha, tornando a saída bastante feia.

Aqui está a textarea que estou acessando:

<textarea id="post-text" class="form-control" rows="3" placeholder="What's up?" required></textarea>

Aqui está o código JavaScript acessando a postagem e transcrevendo-a.

var post = document.createElement('p');
var postText = document.getElementById('post-text').value;
post.append(postText);
var card = document.createElement('div');
card.append(post);
var cardStack = document.getElementById('#card-stack');
cardStack.prepend(card);

Quando a entrada é algo como:

Programação do grupo:

Prática de terça-feira no 5º andar (20h - 23h)

Prática de quinta-feira no 5º andar (20h - 23h)

Prática de domingo @ (21h - 12h)

O resultado é:

Programação do Grupo: Prática de terça-feira no 5º andar (20h - 23h) Prática de quinta-feira no 5º andar (20h - 23h) Prática de domingo @ (21h - 12h)

Então, há uma maneira de preservar as quebras de linha?

Ethan Vander Horn
fonte
1
se você tiver permissão para usar o jQuery, isso pode ajudá-lo: copy-from-textarea-to-div-preserving-linebreaks, alternativamente, isso pode ajudar a preservar as quebras de linha-na-textarea-input
Kevin Kloet
1
As quebras de linha, na verdade, são preservadas, o elemento de destino é apenas configurado para ignorá-las enquanto exibe o texto, mas se você inspecionar, verá que estão aqui. A resposta de Stefano simplesmente define o objetivo de não mais ignorá-los, provavelmente o melhor caminho a seguir.
spectras
2
Desculpe, mas de onde está a getElementByIdfunção da segunda à última linha? Estou assumindo que você o definiu em outro lugar ou esqueceu o document.
trysis

Respostas:

186

A solução mais fácil é simplesmente estilizar o elemento no qual você está inserindo o texto com a seguinte propriedade CSS:

white-space: pre-wrap;

Esta propriedade faz com que os espaços em branco e as novas linhas nos elementos correspondentes sejam tratados da mesma maneira que dentro de um <textarea>. Ou seja, espaços em branco consecutivos não são recolhidos e as linhas são quebradas em novas linhas explícitas (mas também são quebradas automaticamente se excederem a largura do elemento).

Dado que várias das respostas postadas aqui até agora são vulneráveis ​​à injeção de HTML (por exemplo, porque atribuem entrada de usuário sem escape innerHTML) ou de outra forma com erros, deixe-me dar um exemplo de como fazer isso com segurança e corretamente, com base em seu código original:


Observe que, como seu código original, o snippet acima usa append()e prepend(). No momento em que este livro foi escrito, essas funções ainda são consideradas experimentais e não são totalmente compatíveis com todos os navegadores. Se você deseja estar seguro e permanecer compatível com navegadores mais antigos, pode substituí-los facilmente da seguinte maneira:

  • element.append(otherElement)pode ser substituído por element.appendChild(otherElement);
  • element.prepend(otherElement)pode ser substituído por element.insertBefore(otherElement, element.firstChild);
  • element.append(stringOfText)pode ser substituído por element.appendChild(document.createTextNode(stringOfText));
  • element.prepend(stringOfText)pode ser substituído por element.insertBefore(document.createTextNode(stringOfText), element.firstChild);
  • como um caso especial, se elementestiver vazio, ambos element.append(stringOfText)e element.prepend(stringOfText)podem ser simplesmente substituídos por element.textContent = stringOfText.

Aqui está o mesmo snippet acima, mas sem usar append()ou prepend():


Ps. Se você realmente deseja fazer isso sem usar a white-spacepropriedade CSS , uma solução alternativa seria substituir explicitamente quaisquer caracteres de nova linha no texto por <br>tags HTML. A parte complicada é que, para evitar a introdução de bugs sutis e brechas de segurança em potencial, você deve primeiro escapar de quaisquer metacaracteres HTML (no mínimo, &e <) no texto antes de fazer essa substituição.

Provavelmente, a maneira mais simples e segura de fazer isso é deixar o navegador lidar com o escape de HTML para você, assim:

var post = document.createElement('p');
post.textContent = postText;
post.innerHTML = post.innerHTML.replace(/\n/g, '<br>\n');

Observe que, embora isso conserte as quebras de linha, não impedirá que espaços em branco consecutivos sejam recolhidos pelo renderizador de HTML. É possível (meio que) emular isso, substituindo alguns dos espaços em branco no texto por espaços não quebráveis, mas, honestamente, isso está ficando bastante complicado para algo que pode ser resolvido trivialmente com uma única linha de CSS.

Ilmari Karonen
fonte
2
Esta parece ser a resposta mais abrangente. Por que foi rejeitado? Se contiver informações incorretas, compartilhe.
Ethan Vander Horn
4
espaço em branco: a pré-linha funcionou para mim, já que o pré-wrap tinha recuo na primeira linha.
dev4life
1
Valeu cara. Você fez meu dia
thatman
Então a questão é que consigo ver os espaços em branco na área de texto, mas quando os recebo no final da API e são enviados por e-mail, não preserva os espaços.
Apurva Kunkulol
@ApurvaKunkulol: Isso parece um problema em seu código do lado do servidor. Você poderia fazer uma nova pergunta sobre isso, mas precisaria fornecer um exemplo reproduzível mínimo que demonstrasse o problema.
Ilmari Karonen
23

O contêiner de destino deve ter o white-space:preestilo. Experimente abaixo.

<script>
function copycontent(){
 var content = document.getElementById('ta').value;
 document.getElementById('target').innerText = content;
}
</script>
<textarea id='ta' rows='3'>
  line 1
  line 2
  line 3
</textarea>
<button id='btn' onclick='copycontent();'>
Copy
</button>
<p id='target' style='white-space:pre'>

</p>

Stefano Altieri
fonte
10
pre-wrapou pre-lineseria melhor. preevita quebra de linha, então uma linha muito longa de texto forçará a barra de rolagem horizontal.
Salman A
10
Este código é vulnerável à injeção de HTML, porque você está atribuindo entrada de usuário sem escape para innerHTML. Nesse caso, simplesmente substituir innerHTMLpor textContentresolveria o problema.
Ilmari Karonen
1
@IlmariKaronen Isso vai quebrar as coisas. Você precisa definir innerText e textContent . A configuração apenas textContentnão funcionará no IE (qualquer versão) e a configuração apenas innerTextpode não funcionar no Chrome no futuro e não funcionará no Firefox.
Ismael Miguel
3
@IsmaelMiguel: Isso é estranho - estou postando no IE 11 e funciona muito bem aqui. Aparentemente, o IE tem suporte textContentdesde a versão 9.
Ilmari Karonen
2
@IlmariKaronen Isso é estranho ... Eu não sabia disso. Mas é uma boa ideia incluir, innerTextse você precisar do IE8.
Ismael Miguel
7

function get() {
  var arrayOfRows = document.getElementById("ta").value.split("\n");
  var docfrag = document.createDocumentFragment();
  
  var p = document.getElementById("result");
  while (p.firstChild) {
    p.removeChild(p.firstChild);
  }
  
  arrayOfRows.forEach(function(row, index, array) {
    var span = document.createElement("span");
    span.textContent = row;
    docfrag.appendChild(span);
    if(index < array.length - 1) {
      docfrag.appendChild(document.createElement("br"));
    }
  });

  p.appendChild(docfrag);
}
<textarea id="ta" rows=3></textarea><br>
<button onclick="get()">get</button>
<p id="result"></p>

Você pode dividir as linhas de textarea em uma matriz:

var arrayOfRows = postText.value.split("\n");

Em seguida, use-o para gerar, talvez, mais tags p ...

Kolodi
fonte
3
O que @IlmariKaronen disse: este código está incorreto porque você está atribuindo texto simples ( textarea.value) a uma propriedade que espera HTML ( innerHTML). Este é um tipo de erro que causa problemas que variam de texto quebrado a falhas de segurança. Em vez de usar innerHTML, você pode fazer appendChildcom document.createTextNode(text)e document.createElement('br').
Kos
1
Uma boa maneira de tornar isso mais eficiente é document.createDocumentFragment.
Kos
1
@IlmariKaronen, Kos, não era uma pergunta sobre segurança nem eficiência. Eu o otimizei, no entanto.
Kolodi
3
@misher: O OP pediu uma maneira de preservar as quebras de linha, não uma maneira de preservar as quebras de linha e permitir a injeção de HTML. Obter uma brecha de segurança gratuita quando você pede ajuda para consertar um bug da IU é como obter um tachinha grátis no hambúrguer quando você pede um hambúrguer. De qualquer forma, como seu código "otimizado" não parece mais vulnerável, removi meu voto negativo.
Ilmari Karonen
4

Aqui está uma ideia, pois você pode ter várias novas linhas em uma caixa de texto:

 var text=document.getElementById('post-text').value.split('\n');
 var html = text.join('<br />');

Este valor HTML preservará a nova linha. Espero que isto ajude.

Reza
fonte
1
Isso também produzirá uma mistura de entrada do usuário sem escape e tags HTML, o que criará uma vulnerabilidade de injeção de HTML se a htmlstring resultante for realmente renderizada como HTML.
Ilmari Karonen
@IlmariKaronen - Então a preocupação que você tem é que alguém pode se atacar? O OP não faz menção a nenhum tipo de banco de dados. Como a entrada de um usuário afetaria outro usuário de alguma forma? Talvez isso seja algo que é apenas para os olhos dos OPs e não tem necessidade de saneamento (se é que tem alguma necessidade).
Jesse
1
@ Jesse: Em geral, é difícil ter certeza de que a entrada do usuário vem de uma fonte confiável. Mesmo se não houver maneira de um invasor injetar texto diretamente na área de texto, muitos vírus de mídia social se espalharam convencendo usuários ingênuos por meio de engenharia social a copiar e colar algo em um campo de entrada vulnerável. Além disso, mesmo se a entrada realmente puder ser 100% confiável, esse código ainda distorce qualquer texto que contenha metacaracteres HTML como &ou <. Mesmo que a segurança não seja uma preocupação, isso ainda é um bug.
Ilmari Karonen
@IlmariKaronen - Não estou nem um pouco preocupado com um ataque, mas obrigado por esta informação!
Ethan Vander Horn
3

Você pode definir widthde div usando Javascript e adicione white-space:pre-wrapa p tag, esta pausa o seu conteúdo textarea no final de cada linha.

document.querySelector("button").onclick = function gt(){
var card = document.createElement('div');
card.style.width = "160px";
card.style.background = "#eee";
var post = document.createElement('p');
var postText = document.getElementById('post-text').value;
post.style.whiteSpace = "pre-wrap";
card.append(post);
post.append(postText);
document.body.append(card);
}
<textarea id="post-text" class="form-control" rows="3" placeholder="What's up?" required>
Group Schedule:

Tuesday practice @ 5th floor (8pm - 11 pm)

Thursday practice @ 5th floor (8pm - 11 pm)

Sunday practice @ (9pm - 12 am)</textarea>
<br><br>
<button>Copy!!</button>

frnt
fonte
3

Suponho que você não queira que seu conteúdo de textarea seja analisado como HTML. Nesse caso, você pode apenas defini-lo como texto simples para que o navegador não o trate como HTML e não remova novas linhas. Nenhum CSS ou pré-processamento necessário.

<script>
function copycontent(){
 var content = document.getElementById('ta').value;
 document.getElementById('target').innerText = content;
}
</script>
<textarea id='ta' rows='3'>
  line 1
  line 2
  line 3
</textarea>
<button id='btn' onclick='copycontent();'>
Copy
</button>
<p id='target'></p>

tkausl
fonte
2

Perguntas semelhantes estão aqui

detectar quebras de linha em uma entrada de área de texto

detectar quebra de linha em textarea

Você pode tentar isto:

var submit = document.getElementById('submit');

submit.addEventListener('click', function(){
var textContent = document.querySelector('textarea').value;
  
document.getElementById('output').innerHTML = textContent.replace(/\n/g, '<br/>');
  

});
<textarea cols=30 rows=10 >This is some text
this is another text

Another text again and again</textarea>
<input type='submit' id='submit'>


<p id='output'></p>

document.querySelector('textarea').value;obterá o conteúdo de texto da área de texto e textContent.replace(/\n/g, '<br/>')encontrará todos os caracteres de nova linha no código-fonte /\n/gno conteúdo e os substituirá pela quebra de linha html <br/>.


Outra opção é usar a <pre> tag html . Veja a demonstração abaixo

var submit = document.getElementById('submit');

submit.addEventListener('click', function(){

  var content = '<pre>';
  
  var textContent = document.querySelector('textarea').value;
  
  content += textContent;
  
  content += '</pre>';

  document.getElementById('output').innerHTML = content;

});
<textarea cols=30 rows=10>This is some text
this is another text

Another text again and again </textarea>
<input type='submit' id='submit'>

<div id='output'> </div>

Abk
fonte
2
Por favor, releia a pergunta novamente. O autor da postagem pergunta como preservar as quebras de linha ao colocar o texto de uma textarea em uma página, não dentro de outra textarea.
Jesse
1
Seu primeiro exemplo está cheio de erros e não funciona. (Você nunca atribui o resultado de textContent.replace()a nada.) Seu segundo exemplo é vulnerável ao mesmo bug de injeção de HTML que várias outras respostas, porque você está atribuindo entrada de usuário sem escape para innerHTML(e seu primeiro exemplo seria igualmente vulnerável, se tentasse para realmente fazer algo útil com a saída da replace()chamada).
Ilmari Karonen
Obrigado! O Outlook 2013 realmente sabe o que é <pre>
user1566694