ฉันจะรักษาการแบ่งบรรทัดเมื่อได้รับข้อความจากพื้นที่ข้อความได้อย่างไร


98

ฉันได้รับค่าใน textarea เมื่อผู้ใช้กดส่ง จากนั้นฉันจะนำค่านี้ไปวางไว้ที่อื่นในหน้า อย่างไรก็ตามเมื่อฉันทำสิ่งนี้มันจะสูญเสียอักขระขึ้นบรรทัดใหม่ทำให้ผลลัพธ์ค่อนข้างน่าเกลียด

นี่คือ textarea ที่ฉันกำลังเข้าถึง:

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

เมื่ออินพุตเป็นสิ่งที่ต้องการ:

กำหนดการกลุ่ม:

ซ้อมวันอังคารที่ชั้น 5 (20.00 น. - 23.00 น.)

ซ้อมวันพฤหัสบดีที่ชั้น 5 (20.00 น. - 23.00 น.)

ซ้อมวันอาทิตย์ @ (21.00 น. - 12.00 น.)

ผลลัพธ์คือ:

ตารางเรียนวันอังคารที่ชั้น 5 (20.00 น. - 23.00 น.) ซ้อมวันพฤหัสบดีที่ชั้น 5 (20.00 น. - 23.00 น.) ซ้อมวันอาทิตย์ @ (21.00 - 12.00 น.)

มีวิธีรักษาการแบ่งบรรทัดหรือไม่?


1
หากคุณได้รับอนุญาตให้ใช้ jQuery สิ่งนี้อาจช่วยคุณได้: copy-from-textarea-to-div- preserves-
Kevin Kloet

1
การแบ่งบรรทัดจะถูกเก็บรักษาไว้องค์ประกอบเป้าหมายจะถูกตั้งค่าให้เพิกเฉยในขณะที่แสดงข้อความ แต่ถ้าคุณตรวจสอบคุณจะเห็นว่ามีอยู่ที่นี่ คำตอบของ Stefano เพียงตั้งเป้าหมายที่จะไม่เพิกเฉยต่อพวกเขาอีกต่อไปซึ่งอาจเป็นวิธีที่ดีที่สุด
สเปกตรัม

2
ขออภัยคุณ nitpick แต่getElementByIdฟังก์ชั่นจากบรรทัดที่ 2 ถึงสุดท้ายอยู่ที่ไหน ฉันสมมติว่าคุณกำหนดไว้ที่อื่นหรือคุณลืมdocument.
trysis

คำตอบ:


187

วิธีแก้ปัญหาที่ง่ายที่สุดคือจัดรูปแบบองค์ประกอบที่คุณกำลังแทรกข้อความด้วยคุณสมบัติ CSS ต่อไปนี้:

white-space: pre-wrap;

คุณสมบัตินี้ทำให้ช่องว่างและขึ้นบรรทัดใหม่ภายในองค์ประกอบที่ตรงกันได้รับการปฏิบัติในลักษณะเดียวกับภายใน a <textarea>. นั่นคือช่องว่างที่ต่อเนื่องกันจะไม่ยุบและเส้นจะขาดที่บรรทัดใหม่ที่ชัดเจน (แต่จะถูกพันโดยอัตโนมัติหากเกินความกว้างขององค์ประกอบ)

เนื่องจากคำตอบหลายข้อที่โพสต์ไว้ที่นี่มีความเสี่ยงต่อการแทรก HTML (เช่นเนื่องจากพวกเขากำหนดอินพุตของผู้ใช้ที่ไม่ใช้ Escape innerHTML) หรือข้อผิดพลาดอื่น ๆ ให้ฉันยกตัวอย่างวิธีการดำเนินการนี้อย่างปลอดภัยและถูกต้องตามรหัสเดิมของคุณ:


โปรดทราบว่าเช่นรหัสเดิมของคุณตัวอย่างข้อมูลข้างต้นใช้และ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():


Ps. หากคุณต้องการทำสิ่งนี้โดยไม่ใช้white-spaceคุณสมบัติCSS จริงๆทางเลือกอื่นคือแทนที่อักขระขึ้นบรรทัดใหม่ในข้อความด้วย<br>แท็ก HTML อย่างชัดเจน ส่วนที่ยุ่งยากคือเพื่อหลีกเลี่ยงการแนะนำจุดบกพร่องที่ละเอียดอ่อนและช่องโหว่ด้านความปลอดภัยที่อาจเกิดขึ้นก่อนอื่นคุณต้องหลีกเลี่ยงอักขระ HTML ใด ๆ (อย่างน้อยที่สุด&และ<) ในข้อความก่อนที่คุณจะทำการแทนที่นี้

อาจเป็นวิธีที่ง่ายและปลอดภัยที่สุดคือให้เบราว์เซอร์จัดการการหลบหนี HTML ให้คุณดังนี้

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

โปรดทราบว่าแม้ว่าวิธีนี้จะแก้ไขการแบ่งบรรทัด แต่จะไม่ป้องกันไม่ให้ช่องว่างที่ติดต่อกันถูกยุบโดยตัวแสดงผล HTML เป็นไปได้ที่จะ (เรียงลำดับ) เลียนแบบโดยการแทนที่ช่องว่างบางส่วนในข้อความด้วยช่องว่างที่ไม่ทำลาย แต่จริงๆแล้วมันค่อนข้างซับซ้อนสำหรับบางสิ่งที่สามารถแก้ไขได้เล็กน้อยด้วย CSS บรรทัดเดียว


2
นี่น่าจะเป็นคำตอบที่ครอบคลุมที่สุด ทำไมถึงโหวตลง? หากมีข้อมูลที่ไม่ถูกต้องโปรดแบ่งปัน
Ethan Vander Horn

4
white-space: pre-line ใช้ได้ผลสำหรับฉันเนื่องจาก pre-wrap มีการเยื้องในบรรทัดแรก
dev4life

1
ขอบคุณครับ. คุณทำให้วันของฉัน
นั้น

ดังนั้นสิ่งที่ฉันสามารถเห็นช่องว่างในพื้นที่ข้อความ แต่เมื่อฉันได้รับพวกเขาในส่วนท้ายของ API และพวกเขาถูกส่งไปยังอีเมลมันไม่ได้รักษาช่องว่างไว้
Apurva Kunkulol

@ApurvaKunkulol: ดูเหมือนว่าจะมีปัญหาในโค้ดฝั่งเซิร์ฟเวอร์ของคุณ คุณสามารถถามคำถามใหม่เกี่ยวกับเรื่องนี้ได้ แต่คุณต้องให้ตัวอย่างที่ทำซ้ำได้เพียงเล็กน้อยซึ่งแสดงให้เห็นถึงปัญหา
Ilmari Karonen

23

คอนเทนเนอร์เป้าหมายควรมีwhite-space:preลักษณะ ลองดูด้านล่าง

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


10
pre-wrapหรือpre-lineจะดีกว่า preป้องกันการตัดบรรทัดดังนั้นข้อความบรรทัดที่ยาวมากจะบังคับแถบเลื่อนแนวนอน
Salman A

10
รหัสนี้จะมีความเสี่ยงที่จะฉีด HTML, เพราะคุณกำลังกำหนดเข้าของผู้ใช้ที่ไม่ใช้ Escape innerHTMLไป ในกรณีนี้เพียงแค่แทนที่innerHTMLด้วยtextContentก็จะแก้ไขได้
Ilmari Karonen

1
@IlmariKaronen ที่จะทำลายสิ่งต่างๆ คุณจะต้องตั้งและinnerText textContentการตั้งค่า textContentจะใช้ไม่ได้ใน IE เท่านั้น (ทุกเวอร์ชัน) และการตั้งค่าเท่านั้นinnerTextอาจใช้ไม่ได้กับ Chrome ในอนาคตและจะไม่ทำงานบน Firefox
Ismael Miguel

3
@IsmaelMiguel: มันแปลกมาก - ฉันโพสต์สิ่งนี้จาก IE 11 และมันก็ใช้ได้ดีที่นี่ เห็นได้ชัดว่า IE รองรับtextContentตั้งแต่เวอร์ชัน 9
Ilmari Karonen

2
@IlmariKaronen นั่นแปลก ... ฉันไม่รู้เรื่องนั้น แต่ควรรวมไว้innerTextหากคุณต้องการ 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>

คุณสามารถแบ่งแถว textarea เป็นอาร์เรย์:

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

จากนั้นใช้เพื่อสร้างแท็ก p เพิ่มเติม ...


3
สิ่งที่ @IlmariKaronen กล่าว: รหัสนี้ไม่ถูกต้องเนื่องจากคุณกำลังกำหนดข้อความธรรมดา ( textarea.value) ให้กับคุณสมบัติที่คาดว่าจะเป็น HTML ( innerHTML) นี่เป็นข้อผิดพลาดประเภทที่ทำให้เกิดปัญหาตั้งแต่ข้อความเสียไปจนถึงช่องโหว่ด้านความปลอดภัย แทนการใช้innerHTMLที่คุณสามารถทำได้appendChildด้วยและdocument.createTextNode(text) document.createElement('br')
คอส

1
document.createDocumentFragmentวิธีที่ดีที่จะทำให้มีประสิทธิภาพมากขึ้น
คอส

1
@IlmariKaronen, Kos นี่ไม่ใช่คำถามเกี่ยวกับความปลอดภัยหรือประสิทธิภาพ ฉันปรับให้เหมาะสมแล้ว
kolodi

3
@misher: OP ขอวิธีรักษาการแบ่งบรรทัดไม่ใช่วิธีรักษาการแบ่งบรรทัดและอนุญาตให้มีการแทรก HTML การได้รับช่องโหว่ด้านความปลอดภัยฟรีเมื่อคุณขอความช่วยเหลือในการแก้ไขข้อบกพร่องของ UI ก็เหมือนกับการได้รับเป๊กฟรีในขนมพายเมื่อคุณขอแฮมเบอร์เกอร์ อย่างไรก็ตามเนื่องจากโค้ด "ปรับให้เหมาะสม" ของคุณดูเหมือนจะไม่มีช่องโหว่อีกต่อไปฉันจึงลบการลงคะแนนของฉันออกไป
Ilmari Karonen

4

นี่คือแนวคิดเนื่องจากคุณอาจมีการขึ้นบรรทัดใหม่หลายรายการในกล่องข้อความ:

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

ค่า HTML นี้จะรักษาขึ้นบรรทัดใหม่ หวังว่านี่จะช่วยได้


1
นอกจากนี้ยังสร้างการผสมผสานของอินพุตผู้ใช้ที่ไม่ใช้ Escape และแท็ก HTML ซึ่งจะสร้างช่องโหว่ในการแทรก HTML หากhtmlสตริงผลลัพธ์ถูกแสดงเป็น HTML จริง
Ilmari Karonen

@IlmariKaronen - ดังนั้นสิ่งที่คุณกังวลคืออาจมีคนโจมตีตัวเอง? OP ไม่ได้กล่าวถึงฐานข้อมูลประเภทใด ๆ ข้อมูลที่ผู้ใช้ป้อนจะส่งผลต่อผู้ใช้รายอื่นอย่างไร บางทีนี่อาจเป็นสิ่งที่มีไว้สำหรับ OPs eyes เท่านั้นและมีความจำเป็นในการสุขาภิบาล 0 (หากจำเป็นต้องใช้เลยก็ตาม)
Jesse

1
@Jesse: โดยทั่วไปยากที่จะแน่ใจว่าข้อมูลที่ผู้ใช้ป้อนมาจากแหล่งที่เชื่อถือได้ แม้ว่าจะไม่มีวิธีใดที่ผู้โจมตีจะฉีดข้อความเข้าไปในพื้นที่ข้อความโดยตรง แต่ก็มีไวรัสโซเชียลมีเดียจำนวนมากที่แพร่กระจายโดยการโน้มน้าวให้ผู้ใช้ที่ไร้เดียงสาผ่านทางวิศวกรรมสังคมเพื่อคัดลอกและวางบางสิ่งลงในช่องป้อนข้อมูลที่มีช่องโหว่ นอกจากนี้แม้กระทั่งถ้าใส่จริงๆสามารถเชื่อถือได้ 100% รหัสนี้ยังคงปู้ยี่ปู้ยำข้อความใด ๆ ที่มี metacharacters HTML เหมือนหรือ& <แม้ว่าความปลอดภัยจะไม่น่ากังวล แต่นั่นก็ยังเป็นข้อบกพร่อง
Ilmari Karonen

@IlmariKaronen - ฉันไม่ได้กังวลเกี่ยวกับการโจมตี แต่ขอขอบคุณสำหรับข้อมูลนี้!
Ethan Vander Horn

3

คุณสามารถตั้งค่าwidthdiv โดยใช้ Javascript และเพิ่มwhite-space:pre-wrapเข้าไปp tagซึ่งจะแบ่งเนื้อหา textarea ของคุณในตอนท้ายของแต่ละบรรทัด

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>


2

คำถามที่คล้ายกันอยู่ที่นี่

ตรวจจับการแบ่งบรรทัดในการป้อนพื้นที่ข้อความ

ตรวจจับการแบ่งบรรทัดใน textarea

คุณสามารถลองสิ่งนี้:

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;จะได้รับเนื้อหาข้อความของ textarea และtextContent.replace(/\n/g, '<br/>')จะพบทุกตัวอักษรขึ้นบรรทัดใหม่ในรหัสที่มา/\n/gในเนื้อหาและแทนที่ด้วย html <br/>ที่เส้นแบ่ง


อีกทางเลือกหนึ่งคือการใช้ <pre>แท็กhtml ดูการสาธิตด้านล่าง

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 เช่นเดียวกับคำตอบอื่น ๆ เนื่องจากคุณกำลังกำหนดอินพุตของผู้ใช้ที่ไม่ใช้ Escape innerHTML(และตัวอย่างแรกของคุณจะมีความเสี่ยงเท่ากันหากคุณลอง เพื่อทำสิ่งที่เป็นประโยชน์กับผลลัพธ์ของการreplace()โทร)
Ilmari Karonen

ขอขอบคุณ! Outlook 2013 รู้จริงว่า <pre> คืออะไร
user1566694
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.