151

I am getting the value in a textarea when the user hits submit. I then take this value and place it elsewhere on the page. However, when I do this, it loses newline characters making the output pretty ugly.

Here is the textarea I am accessing:

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

Here is the JavaScript code accessing the post and transcribing it.

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

When the input is something like:

Group Schedule:

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

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

Sunday practice @ (9 pm - 12 am)

The output is:

Group Schedule: Tuesday practice @ 5th floor (8 pm - 11 pm) Thursday practice @ 5th floor (8 pm - 11 pm) Sunday practice @ (9 pm - 12 am)

So is there a way to preserve line breaks?

Community
  • 1
  • 1
Ethan Vander Horn
  • 1,513
  • 2
  • 8
  • 7
  • 1
    if you are allowed to use jQuery this might help you: [copy-from-textarea-to-div-preserving-linebreaks](http://stackoverflow.com/questions/16165286/copy-from-textarea-to-div-preserving-linebreaks) alternatively this might help [preserve-line-breaks-in-textarea-input](http://stackoverflow.com/questions/30593103/preserve-line-breaks-in-textarea-input) – Kevin Kloet Nov 04 '16 at 07:45
  • 1
    Line breaks actually are preserved, the target element is just set to ignore them while displaying the text but if you inspect it you will see they are here. Stefano's answer simply sets the target to no longer ignore them, probably the best way to go. – spectras Nov 04 '16 at 16:11
  • 2
    Sorry to nitpick, but where is the `getElementById` function from on the 2nd-to-last line? I'm assuming either you defined it elsewhere or you forgot the `document`. – trysis Nov 04 '16 at 19:55

7 Answers7

301

The easiest solution is to simply style the element you're inserting the text into with the following CSS property:

white-space: pre-wrap;

This property causes whitespace and newlines within the matching elements to be treated in the same way as inside a <textarea>. That is, consecutive whitespace is not collapsed, and lines are broken at explicit newlines (but are also wrapped automatically if they exceed the width of the element).

Given that several of the answers posted here so far have been vulnerable to HTML injection (e.g. because they assign unescaped user input to innerHTML) or otherwise buggy, let me give an example of how to do this safely and correctly, based on your original code:

document.getElementById('post-button').addEventListener('click', function () {
  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);
});
#card-stack p {
  background: #ddd;
  white-space: pre-wrap;  /* <-- THIS PRESERVES THE LINE BREAKS */
}
textarea {
  width: 100%;
}
<textarea id="post-text" class="form-control" rows="8" 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>
<input type="button" id="post-button" value="Post!">
<div id="card-stack"></div>

Note that, like your original code, the snippet above uses append() and prepend(). As of this writing, those functions are still considered experimental and not fully supported by all browsers. If you want to be safe and remain compatible with older browsers, you can substitute them pretty easily as follows:

  • element.append(otherElement) can be replaced with element.appendChild(otherElement);
  • element.prepend(otherElement) can be replaced with element.insertBefore(otherElement, element.firstChild);
  • element.append(stringOfText) can be replaced with element.appendChild(document.createTextNode(stringOfText));
  • element.prepend(stringOfText) can be replaced with element.insertBefore(document.createTextNode(stringOfText), element.firstChild);
  • as a special case, if element is empty, both element.append(stringOfText) and element.prepend(stringOfText) can simply be replaced with element.textContent = stringOfText.

Here's the same snippet as above, but without using append() or prepend():

document.getElementById('post-button').addEventListener('click', function () {
  var post = document.createElement('p');
  var postText = document.getElementById('post-text').value;
  post.textContent = postText;
  var card = document.createElement('div');
  card.appendChild(post);
  var cardStack = document.getElementById('card-stack');
  cardStack.insertBefore(card, cardStack.firstChild);
});
#card-stack p {
  background: #ddd;
  white-space: pre-wrap;  /* <-- THIS PRESERVES THE LINE BREAKS */
}
textarea {
  width: 100%;
}
<textarea id="post-text" class="form-control" rows="8" 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>
<input type="button" id="post-button" value="Post!">
<div id="card-stack"></div>

Ps. If you really want to do this without using the CSS white-space property, an alternative solution would be to explicitly replace any newline characters in the text with <br> HTML tags. The tricky part is that, to avoid introducing subtle bugs and potential security holes, you have to first escape any HTML metacharacters (at a minimum, & and <) in the text before you do this replacement.

Probably the simplest and safest way to do that is to let the browser handle the HTML-escaping for you, like this:

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

document.getElementById('post-button').addEventListener('click', function () {
  var post = document.createElement('p');
  var postText = document.getElementById('post-text').value;
  post.textContent = postText;
  post.innerHTML = post.innerHTML.replace(/\n/g, '<br>\n');  // <-- THIS FIXES THE LINE BREAKS
  var card = document.createElement('div');
  card.appendChild(post);
  var cardStack = document.getElementById('card-stack');
  cardStack.insertBefore(card, cardStack.firstChild);
});
#card-stack p {
  background: #ddd;
}
textarea {
  width: 100%;
}
<textarea id="post-text" class="form-control" rows="8" 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>
<input type="button" id="post-button" value="Post!">
<div id="card-stack"></div>

Note that, while this will fix the line breaks, it won't prevent consecutive whitespace from being collapsed by the HTML renderer. It's possible to (sort of) emulate that by replacing some of the whitespace in the text with non-breaking spaces, but honestly, that's getting rather complicated for something that can be trivially solved with a single line of CSS.

Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • 2
    This seems to be the most comprehensive answer. Why is it down voted? If it contains incorrect information please share. – Ethan Vander Horn Nov 18 '16 at 04:29
  • 7
    white-space: pre-line worked for me, as pre-wrap had indention on the first line. – dev4life Apr 25 '17 at 19:08
  • So the thing is that I am able to see the whitespaces in the text area, but when I receive them on the API end and they're sent into an email, it doesn't preserve the spaces. – Apurva Kunkulol Dec 20 '18 at 11:58
  • @ApurvaKunkulol: That sounds like a problem in your server-side code. You could ask a new question about it, but you'd need to provide a [mcve] that demonstrates the problem. – Ilmari Karonen Dec 20 '18 at 12:01
28

The target container should have the white-space:pre style. Try it below.

<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
  • 4,550
  • 1
  • 24
  • 41
  • Yes, this is better for a single p tag. – kolodi Nov 04 '16 at 08:33
  • 11
    `pre-wrap` or `pre-line` would be better. `pre` prevents line wrapping so a very long line of text will force horizontal scrollbar. – Salman A Nov 04 '16 at 09:58
  • 10
    This code is vulnerable to HTML injection, because you're assigning unescaped user input to `innerHTML`. In this case, simply replacing `innerHTML` with `textContent` would fix it. – Ilmari Karonen Nov 04 '16 at 14:20
  • 1
    @IlmariKaronen That will break things. You need to set `innerText` **and** `textContent`. Setting only `textContent` won't work in IE (any version) and setting only `innerText` may not work on Chrome in the future and will not work on Firefox. – Ismael Miguel Nov 04 '16 at 16:17
  • 3
    @IsmaelMiguel: That's strange -- I'm posting this from IE 11, and it works just fine here. [Apparently](https://msdn.microsoft.com/en-us/library/ff974773(v=vs.85).aspx) IE has supported `textContent` since version 9. – Ilmari Karonen Nov 04 '16 at 16:45
  • 2
    @IlmariKaronen That is strange... I wasn't aware of that. But it is a good idea to include `innerText` if you need IE8. – Ismael Miguel Nov 04 '16 at 17:22
  • 1
    thanks @IlmariKaronen and IsmaelMiguel for your suggestions. I updated to use innerText – Stefano Altieri Nov 05 '16 at 08:51
  • Thanks, downvote removed. (I would still prefer `textContent` over `innerText` just because it's standardized, possibly with a polyfill for IE8 if you really need to keep supporting that. But the fact is that all major modern browsers -- even Firefox since version 49 -- do support both, and are unlikely to drop either any time soon, because it would break too much existing code.) – Ilmari Karonen Nov 05 '16 at 13:43
9

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>

You can split textarea rows into array:

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

Then use it to generate, maybe, more p tags...

kolodi
  • 1,002
  • 9
  • 16
  • 3
    What @IlmariKaronen said: this code is incorrect because you're assigning plaintext (`textarea.value`) to a property that expects HTML (`innerHTML`). This is a type error that causes problems ranged from broken text to security holes. Instead of using `innerHTML`, you can do `appendChild` with `document.createTextNode(text)` and `document.createElement('br')`. – Kos Nov 04 '16 at 14:24
  • 2
    A good way to make that more efficient is [`document.createDocumentFragment`](https://developer.mozilla.org/en/docs/Web/API/Document/createDocumentFragment). – Kos Nov 04 '16 at 14:25
  • 1
    @IlmariKaronen, Kos, this was not a question about security nor eficiency. I did optimize it, though. – kolodi Nov 04 '16 at 15:17
  • 3
    @misher: The OP asked for a way to preserve line breaks, not for a way to preserve line breaks *and* allow HTML injection. Getting a free security hole when you ask for help to fix a UI bug is kind of like getting a free thumbtack in the patty when you ask for a hamburger. Anyway, since your "optimized" code seems no longer vulnerable, I've removed my downvote. – Ilmari Karonen Nov 04 '16 at 15:46
5

Here is an idea as you may have multiple newline in a textbox:

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

This HTML value will preserve newline. Hope this helps.

joan16v
  • 5,055
  • 4
  • 49
  • 49
reza
  • 1,507
  • 2
  • 12
  • 17
  • 1
    This will also produce a mix of unescaped user input and HTML tags, which will create an HTML injection vulnerability if the resulting `html` string is ever actually rendered as HTML. – Ilmari Karonen Nov 04 '16 at 15:50
  • @IlmariKaronen - So the concern you have is that someone may attack themselves? The OP makes no mention of any type of database. How would one users input affect another user in any way? Maybe this is something that is only for the OPs eyes only and has 0 need for sanitation (if it even has any need for it at all). – Jesse Nov 05 '16 at 02:26
  • 1
    @Jesse: In general, it's hard to be sure that user input comes from a trusted source. Even if there's no way for an attacker to directly inject text into the textarea, there have been plenty of social media viruses that have spread by convincing naïve users via social engineering to copy-paste something into a vulnerable entry field. Besides, even if the input really can be trusted 100%, this code still mangles any text that contains HTML metacharacters like `&` or `<`. Even if security is not a concern, that's still a bug. – Ilmari Karonen Nov 05 '16 at 05:13
  • @IlmariKaronen - I am not at all concerned about an attack but thank you for this information! – Ethan Vander Horn Nov 18 '16 at 04:26
3

Similar questions are here

detect line breaks in a text area input

detect line break in textarea

You can try this:

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; will get the text content of the textarea and textContent.replace(/\n/g, '<br/>') will find all the newline character in the source code /\n/g in the content and replace it with the html line-break <br/>.


Another option is to use the html <pre> tag. See the demo below

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>
Community
  • 1
  • 1
Abk
  • 2,137
  • 1
  • 23
  • 33
  • 2
    Please reread the question again. The poster is asking how to preserve line breaks when putting text from a textarea on a page, not inside another textarea. – Jesse Nov 04 '16 at 08:02
  • 1
    Your first example is buggy and does not work. (You never even assign the result of `textContent.replace()` to anything.) Your second example is vulnerable to the same HTML injection bug as several other answers, because you're assigning unescaped user input to `innerHTML` (and you first example would be equally vulnerable, if you tried to actually do anything useful with the output of the `replace()` call). – Ilmari Karonen Nov 04 '16 at 14:39
  • Thank you! Outlook 2013 actually knows what
     is
    – user1566694 Feb 20 '19 at 22:40
3

You could set width of div using Javascript and add white-space:pre-wrap to p tag, this break your textarea content at end of each line.

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
  • 8,455
  • 2
  • 22
  • 25
3

I suppose you don't want your textarea-content to be parsed as HTML. In this case, you can just set it as plaintext so the browser doesn't treat it as HTML and doesn't remove newlines No CSS or preprocessing required.

<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
  • 13,686
  • 2
  • 33
  • 50