¿Cómo conservo los saltos de línea al obtener texto de un área de texto?

96

Obtengo el valor en un área de texto cuando el usuario presiona enviar. Luego tomo este valor y lo coloco en otra parte de la página. Sin embargo, cuando hago esto, pierde caracteres de nueva línea, lo que hace que el resultado sea bastante feo.

Aquí está el área de texto al que estoy accediendo:

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

Aquí está el código JavaScript que accede a la publicación y la transcribe.

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

Cuando la entrada es algo como:

Horario de grupo:

Práctica del martes en el quinto piso (8 pm - 11 pm)

Práctica del jueves en el quinto piso (8 pm - 11 pm)

Práctica dominical @ (9 pm - 12 am)

La salida es:

Horario grupal: práctica del martes en el quinto piso (8 p.m. a 11 p.m.) práctica del jueves en el quinto piso (8 p.m. a 11 p.m.) práctica del domingo (9 p.m. a 12 a.m.)

Entonces, ¿hay alguna forma de preservar los saltos de línea?

Ethan Vander Horn
fuente
1
si se le permite usar jQuery, esto podría ayudarlo: copy-from-textarea-to-div-preserving-linebreaks, alternativamente, esto podría ayudar a preservar-line-breaks-in-textarea-input
Kevin Kloet
1
Los saltos de línea en realidad se conservan, el elemento de destino está configurado para ignorarlos mientras muestra el texto, pero si lo inspecciona, verá que están aquí. La respuesta de Stefano simplemente establece que el objetivo ya no los ignore, probablemente la mejor manera de hacerlo.
espectros
2
Lamento ser quisquilloso, pero ¿de dónde está la getElementByIdfunción en la segunda a la última línea? Supongo que lo definió en otro lugar o que olvidó el document.
Trysis

Respuestas:

175

La solución más fácil es simplemente diseñar el elemento en el que está insertando el texto con la siguiente propiedad CSS:

white-space: pre-wrap;

Esta propiedad hace que los espacios en blanco y las nuevas líneas dentro de los elementos coincidentes se traten de la misma manera que dentro de a <textarea>. Es decir, los espacios en blanco consecutivos no se contraen y las líneas se rompen en nuevas líneas explícitas (pero también se ajustan automáticamente si exceden el ancho del elemento).

Dado que varias de las respuestas publicadas aquí hasta ahora han sido vulnerables a la inyección de HTML (por ejemplo, porque asignan una entrada de usuario sin escape innerHTML) o tienen errores, permítanme dar un ejemplo de cómo hacer esto de manera segura y correcta, según su código original:


Tenga en cuenta que, al igual que su código original, el fragmento anterior usa append()y prepend(). En el momento de escribir este artículo, esas funciones todavía se consideran experimentales y no son totalmente compatibles con todos los navegadores. Si desea estar seguro y seguir siendo compatible con navegadores más antiguos, puede sustituirlos con bastante facilidad de la siguiente manera:

  • element.append(otherElement)se puede reemplazar con element.appendChild(otherElement);
  • element.prepend(otherElement)se puede reemplazar con element.insertBefore(otherElement, element.firstChild);
  • element.append(stringOfText)se puede reemplazar con element.appendChild(document.createTextNode(stringOfText));
  • element.prepend(stringOfText)se puede reemplazar con element.insertBefore(document.createTextNode(stringOfText), element.firstChild);
  • como caso especial, si elementestá vacío, ambos element.append(stringOfText)y element.prepend(stringOfText)simplemente se pueden reemplazar con element.textContent = stringOfText.

Este es el mismo fragmento que el anterior, pero sin usar append()o prepend():


PD. Si realmente desea hacer esto sin usar la white-spacepropiedad CSS , una solución alternativa sería reemplazar explícitamente cualquier carácter de nueva línea en el texto con <br>etiquetas HTML. La parte difícil es que, para evitar la introducción de errores sutiles y agujeros de seguridad potenciales, usted tiene que escapar primero cualquier meta HTML (como mínimo, &y <) en el texto antes de hacer esto reemplazo.

Probablemente la forma más sencilla y segura de hacerlo es dejar que el navegador maneje el escape HTML por usted, así:

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

Tenga en cuenta que, aunque esto arreglará los saltos de línea, no evitará que el renderizador HTML colapse espacios en blanco consecutivos. Es posible (más o menos) emular eso reemplazando algunos de los espacios en blanco en el texto con espacios que no se rompan, pero honestamente, eso se está volviendo bastante complicado para algo que se puede resolver trivialmente con una sola línea de CSS.

Ilmari Karonen
fuente
2
Esta parece ser la respuesta más completa. ¿Por qué se votó en contra? Si contiene información incorrecta, compártala.
Ethan Vander Horn
4
espacio en blanco: la línea previa funcionó para mí, ya que el ajuste previo tenía una sangría en la primera línea.
dev4life
1
Gracias amigo. Me
alegraste el
Entonces, la cosa es que puedo ver los espacios en blanco en el área de texto, pero cuando los recibo en el extremo de la API y se envían a un correo electrónico, no conserva los espacios.
Apurva Kunkulol
@ApurvaKunkulol: Eso suena como un problema en su código del lado del servidor. Puede hacer una nueva pregunta al respecto, pero debe proporcionar un ejemplo mínimo reproducible que demuestre el problema.
Ilmari Karonen
23

El contenedor de destino debe tener el white-space:preestilo. Pruébelo a continuación.

<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
fuente
10
pre-wrapo pre-linesería mejor. preevita el ajuste de línea, por lo que una línea de texto muy larga forzará la barra de desplazamiento horizontal.
Salman A
10
Este código es vulnerable a la inyección de HTML, porque está asignando una entrada de usuario sin escape a innerHTML. En este caso, simplemente reemplazarlo innerHTMLcon textContentlo solucionaría.
Ilmari Karonen
1
@IlmariKaronen Eso romperá las cosas. Necesita configurar innerText y textContent . La configuración solo textContentno funcionará en IE (cualquier versión) y la configuración solo innerTextpuede no funcionar en Chrome en el futuro y no funcionará en Firefox.
Ismael Miguel
3
@IsmaelMiguel: Eso es extraño, estoy publicando esto desde IE 11 y funciona bien aquí. Aparentemente, IE ha sido compatible textContentdesde la versión 9.
Ilmari Karonen
2
@IlmariKaronen Eso es extraño ... No era consciente de eso. Pero es una buena idea incluirlo innerTextsi necesita 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>

Puede dividir filas de área de texto en una matriz:

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

Luego úselo para generar, tal vez, más etiquetas p ...

Kolodi
fuente
3
Lo que dijo @IlmariKaronen: este código es incorrecto porque está asignando texto sin formato ( textarea.value) a una propiedad que espera HTML ( innerHTML). Este es un tipo de error que causa problemas que van desde texto roto hasta agujeros de seguridad. En lugar de usar innerHTML, puede hacerlo appendChildcon document.createTextNode(text)y document.createElement('br').
Kos
1
Una buena forma de hacerlo más eficiente es document.createDocumentFragment.
Kos
1
@IlmariKaronen, Kos, esta no era una cuestión de seguridad ni de eficiencia. Sin embargo, lo optimicé.
kolodi
3
@misher: El OP pidió una forma de preservar los saltos de línea, no una forma de preservar los saltos de línea y permitir la inyección de HTML. Obtener un agujero de seguridad gratuito cuando pide ayuda para solucionar un error de la interfaz de usuario es como obtener una chincheta gratis en la hamburguesa cuando pide una hamburguesa. De todos modos, dado que su código "optimizado" ya no parece vulnerable, eliminé mi voto negativo.
Ilmari Karonen
3

Aquí hay una idea, ya que puede tener varias líneas nuevas en un cuadro de texto:

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

Este valor HTML conservará la nueva línea. Espero que esto ayude.

reza
fuente
1
Esto también producirá una combinación de entrada de usuario sin escape y etiquetas HTML, lo que creará una vulnerabilidad de inyección de HTML si la htmlcadena resultante alguna vez se renderiza como HTML.
Ilmari Karonen
@IlmariKaronen - Entonces, ¿la preocupación que tienes es que alguien pueda atacarse a sí mismo? El OP no menciona ningún tipo de base de datos. ¿Cómo afectaría la entrada de un usuario a otro usuario de alguna manera? Tal vez esto es algo que es solo para los ojos de los OP y no tiene ninguna necesidad de saneamiento (si es que tiene alguna necesidad).
Jesse
1
@Jesse: En general, es difícil estar seguro de que la entrada del usuario proviene de una fuente confiable. Incluso si no hay forma de que un atacante inyecte texto directamente en el área de texto, ha habido muchos virus de redes sociales que se han propagado al convencer a usuarios ingenuos a través de la ingeniería social de copiar y pegar algo en un campo de entrada vulnerable. Además, incluso si realmente se puede confiar al 100% en la entrada, este código aún destruye cualquier texto que contenga metacaracteres HTML como &o <. Incluso si la seguridad no es una preocupación, sigue siendo un error.
Ilmari Karonen
@IlmariKaronen - ¡No me preocupa en absoluto un ataque, pero gracias por esta información!
Ethan Vander Horn
3

También podemos establecer widthde div usando Javascript y añadir white-space:pre-wrapa p tag, esta ruptura de su contenido en el área de texto final de cada línea.

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

Supongo que no desea que el contenido de su área de texto se analice como HTML. En este caso, puede configurarlo como texto sin formato para que el navegador no lo trate como HTML y no elimine las líneas nuevas. No se requiere CSS ni preprocesamiento.

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

Preguntas similares están aquí

detectar saltos de línea en una entrada de área de texto

detectar salto de línea en el área de texto

Puedes probar esto:

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;obtendrá el contenido de texto del área de texto y textContent.replace(/\n/g, '<br/>')encontrará todo el carácter de nueva línea en el código fuente /\n/gen el contenido y lo reemplazará con el salto de línea html <br/>.


Otra opción es usar la <pre> etiqueta html . Vea la demostración a continuación

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
fuente
2
Vuelva a leer la pregunta. El cartel pregunta cómo conservar los saltos de línea al colocar texto de un área de texto en una página, no dentro de otra área de texto.
Jesse
1
Su primer ejemplo tiene errores y no funciona. (Ni siquiera asigna el resultado de textContent.replace()a nada.) Su segundo ejemplo es vulnerable al mismo error de inyección de HTML que otras respuestas, porque está asignando una entrada de usuario sin escape a innerHTML(y su primer ejemplo sería igualmente vulnerable, si lo intentara para hacer algo útil con el resultado de la replace()llamada).
Ilmari Karonen
¡Gracias! Outlook 2013 realmente sabe lo que es <pre>
user1566694