当从文本区域获取文本时,如何保留换行符?

155

当用户点击提交按钮时,我正在获取文本区域中的值。然后,我将此值放置在页面的其他位置。但是,这样做会使输出失去换行符,从而使输出结果变得不太美观。

这里是我正在访问的文本区域:

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

以下是JavaScript代码,用于访问帖子并将其转录。

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

当输入如下:

组织日程:

周二练习 @ 五楼 (晚上8点至11点)

周四练习 @ 五楼 (晚上8点至11点)

周日练习 @ (晚上9点至12点)

输出结果如下:

组织日程:周二练习 @ 五楼 (晚上8点至11点) 周四练习 @ 五楼 (晚上8点至11点) 周日练习 @ (晚上9点至12点)

有没有一种方法可以保留换行符?


1
如果您可以使用jQuery,这可能会对您有所帮助:copy-from-textarea-to-div-preserving-linebreaks 或者您也可以尝试这个方法:preserve-line-breaks-in-textarea-input - Kevin Kloet
1
换行符实际上是被保留的,目标元素只是在显示文本时将其设置为忽略,但是如果您检查它,就会发现它们在这里。Stefano的答案只是将目标设置为不再忽略它们,可能是最好的方法。 - spectras
2
抱歉挑剔一下,但是倒数第二行的 getElementById 函数从哪里来?我猜你要么在别处定义了它,要么忘记加上 document 了。 - trysis
7个回答

313

最简单的解决方案是使用以下CSS属性来对插入文本的元素进行样式设置:

white-space: pre-wrap;

此属性会将匹配元素内的空格和换行符与 <textarea> 元素内的处理方式一样。也就是说,连续的空格不会被折叠,而且明确的换行符会产生新的行(但如果超过了元素的宽度,则也会自动换行)。

鉴于到目前为止发布在这里的答案中有几个容易受到HTML 注入(例如因为它们将未经转义的用户输入分配给 innerHTML),或者存在其他错误,请允许我基于您的原始代码给出一个安全且正确的示例:

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>


需要注意的是,和原始代码一样,上面的示例使用了 append()prepend()。截至撰写此文时,这些函数仍被认为是实验性质的,不被所有浏览器完全支持。如果你希望更安全并与旧版浏览器兼容,可以简单地按如下进行替换:

  • element.append(otherElement) 可以替换为 element.appendChild(otherElement)
  • element.prepend(otherElement) 可以替换为 element.insertBefore(otherElement, element.firstChild)
  • element.append(stringOfText) 可以替换为 element.appendChild(document.createTextNode(stringOfText))
  • element.prepend(stringOfText) 可以替换为 element.insertBefore(document.createTextNode(stringOfText), element.firstChild)
  • 特殊情况下,如果 element 为空,则 element.append(stringOfText)element.prepend(stringOfText) 都可以简单替换为 element.textContent = stringOfText

下面是与上文相同的示例,但不使用 append()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>

顺便提一句,如果你真的想做到这一点而不使用CSS的 white-space属性,则另一种解决方案是显式地将文本中的任何换行符替换为<br> HTML标签。棘手的部分是,为了避免引入微妙的错误和潜在的安全漏洞,您必须首先转义文本中的任何HTML元字符(至少是&<)才能进行此替换。

可能最简单、最安全的做法是让浏览器自动处理HTML转义,像这样:

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>

请注意,虽然这将修复换行符,但它并不能防止HTML渲染器折叠连续的空白。可以通过在文本中用非断行空格替换一些空格来(有点)模拟它,但说实话,对于可以轻松解决的事情,这变得相当复杂。


2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Ethan Vander Horn
7
"white-space: pre-line" 对我有用,因为 "pre-wrap" 在第一行上有缩进。 - dev4life
所以问题是,我能够在文本区域中看到空格,但当它们在API端接收并发送到电子邮件时,它不会保留空格。 - Apurva Kunkulol
@ApurvaKunkulol:这听起来像是你服务器端代码的问题。你可以发一个新问题询问,但需要提供一个 [mcve] 来演示问题。 - Ilmari Karonen

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>


11
使用pre-wrappre-line会更好。pre会防止换行,因此很长的一行文本将强制出现水平滚动条。 - Salman A
10
这段代码存在HTML注入漏洞,因为你将未转义的用户输入赋值给了 innerHTML。在这种情况下,只需将 innerHTML 替换为 textContent 即可修复该问题。 - Ilmari Karonen
1
@IlmariKaronen 那样会破坏事情。您需要设置 innerText textContent。仅设置 textContent 在 IE(任何版本)中不起作用,并且仅在 Chrome 中设置 innerText 可能在将来不起作用,也不适用于 Firefox。 - Ismael Miguel
3
很奇怪——我正在使用IE 11发布这个帖子,它在这里运作良好。显然,IE从9版本开始就支持textContent - Ilmari Karonen
2
@IlmariKaronen 这很奇怪...我不知道这一点。但是如果您需要IE8,包括innerText是一个好主意。 - Ismael Miguel
显示剩余3条评论

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>

您可以将文本区域的行拆分为数组:

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

然后使用它来生成更多的p标签...

3
@IlmariKaronen所说的是:这段代码是错误的,因为你正在将明文(textarea.value)赋值给一个期望HTML(innerHTML)的属性。这是一种类型错误,会导致从破碎的文本到安全漏洞的问题。你可以使用document.createTextNode(text)document.createElement('br')进行appendChild,而不是使用innerHTML - Kos
2
一个使这个过程更加高效的好方法是使用document.createDocumentFragment - Kos
1
@IlmariKaronen,Kos,这不是关于安全或效率的问题。虽然我进行了优化。 - kolodi
3
楼上的提问者要求保留换行符,而不是保留换行符并允许 HTML 注入。在寻求 UI 错误修复帮助时意外获得免费安全漏洞,有点像在索要汉堡时从中获得免费图钉。无论如何,由于你的“优化”代码似乎不再存在漏洞,我已经取消了我的负评。 - Ilmari Karonen

5

如果您在文本框中有多个换行符,这里有一个解决方案:

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

这个 HTML 值会保留换行符。希望这可以帮到你。


1
这也会产生未转义的用户输入和HTML标记混合,如果生成的“html”字符串实际上被呈现为HTML,则会创建HTML注入漏洞。 - Ilmari Karonen
@IlmariKaronen - 所以您担心的是有些人可能会攻击自己?OP并没有提到任何类型的数据库。一个用户的输入如何影响另一个用户呢?也许这只是供 OP 自己查看,完全不需要进行任何清洁(如果它甚至需要)。 - Jesse
1
@Jesse:一般来说,很难确定用户输入是否来自可信的来源。即使攻击者无法直接将文本注入到文本区域中,也有许多社交媒体病毒通过社交工程欺骗天真的用户将某些内容复制粘贴到易受攻击的输入字段中而传播。此外,即使输入确实可以被100%信任,这段代码仍会破坏包含HTML元字符(如&<)的任何文本。即使安全不是问题,这仍然是一个错误。 - Ilmari Karonen
@IlmariKaronen - 我并不担心攻击,但还是谢谢你提供这个信息! - Ethan Vander Horn

3

类似的问题在这里

如何检测文本区域输入框中的换行符

如何检测文本区域中的换行符

您可以尝试这个:

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;可以获取文本区域的文本内容,textContent.replace(/\n/g, '<br/>')会在内容中找到所有源代码中的换行符/\n/g并将其替换为html换行符<br/>
另一种选择是使用html标签<pre>请参见以下演示

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>


2
请再次仔细阅读问题。发帖人询问如何在将文本从文本区域放置到页面上时保留换行符,而不是在另一个文本区域内。 - Jesse
1
你的第一个示例有漏洞,无法正常工作。(你甚至没有将textContent.replace()的结果分配给任何变量。)你的第二个示例容易受到与其他答案相同的HTML注入漏洞的影响,因为你将未经转义的用户输入分配给innerHTML(如果你尝试对replace()调用的输出执行任何有用的操作,那么你的第一个示例也会同样容易受到攻击)。 - Ilmari Karonen
谢谢!Outlook 2013实际上知道<pre>是什么。 - user1566694

3
您可以使用Javascript设置div的宽度,并在p标签中添加"white-space:pre-wrap",这将在每行末尾断开您的文本区域内容。

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>


3

我猜你不希望你的文本区域内容被解析为HTML。在这种情况下,你可以将其设置为纯文本,这样浏览器就不会将其视为HTML并且不会删除换行符。不需要CSS或预处理。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接