Как сохранить разрывы строк при получении текста из текстового поля?

96

Я получаю значение в текстовом поле, когда пользователь нажимает кнопку «Отправить». Затем я беру это значение и помещаю в другое место на странице. Однако, когда я это делаю, он теряет символы новой строки, что делает вывод довольно уродливым.

Вот текстовое поле, к которому я обращаюсь:

<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-preserving-linebreaks в качестве альтернативы это может помочь сохранить-разрывы строк-in-textarea-input
Кевин Клоет
1
Разрывы строк фактически сохраняются, целевой элемент просто настроен так, чтобы игнорировать их при отображении текста, но если вы его проверите, вы увидите, что они здесь. Ответ Стефано просто ставит цель больше не игнорировать их, вероятно, лучший выход.
спектры
2
Извините за придирки, но где находится getElementByIdфункция на предпоследней строке? Я предполагаю, что либо вы определили его в другом месте, либо забыли document.
trysis

Ответы:

175

Самое простое решение - просто стилизовать элемент, в который вы вставляете текст, с помощью следующего свойства CSS:

white-space: pre-wrap;

Это свойство заставляет пробелы и символы новой строки в совпадающих элементах обрабатываться так же, как внутри <textarea>. То есть последовательные пробелы не сворачиваются, а строки разбиваются на явные символы новой строки (но также автоматически переносятся, если они превышают ширину элемента).

Учитывая, что некоторые из ответов, опубликованных здесь до сих пор, были уязвимы для HTML-инъекции (например, потому, что они назначают неэкранированный ввод пользователя 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():


Пс. Если вы действительно хотите сделать это без использования 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
Это кажется наиболее исчерпывающим ответом. Почему проголосовали против? Если он содержит неверную информацию, поделитесь.
Итан Вандер Хорн
4
white-space: pre-line у ​​меня работала, так как pre-wrap имел отступ в первой строке.
dev4life
1
Спасибо чувак. Ты сделал мой день
тотман
Дело в том, что я могу видеть пробелы в текстовой области, но когда я получаю их на стороне API, и они отправляются по электронной почте, пробелы не сохраняются.
Апурва Кункулол
@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-инъекции, потому что вы назначаете неэкранированный пользовательский ввод innerHTML. В этом случае простая замена innerHTMLна textContentпоможет исправить это.
Ilmari Karonen
1
@IlmariKaronen Это все сломает. Вам нужно установить innerText и textContent . Только настройка textContentне будет работать в IE (любой версии), а настройка только innerTextможет не работать в Chrome в будущем и не будет работать в Firefox.
Ismael Miguel
3
@IsmaelMiguel: Странно - я публикую это из IE 11, и здесь он отлично работает. Очевидно, IE поддерживается textContentс версии 9.
Илмари Каронен
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: этот код неверен, потому что вы назначаете plaintext ( textarea.value) свойству, ожидающему HTML ( innerHTML). Это ошибка типа, которая вызывает различные проблемы: от неработающего текста до дыр в безопасности. Вместо использования innerHTML, вы можете сделать appendChildс document.createTextNode(text)и document.createElement('br').
Кос,
1
Хороший способ сделать это более эффективным - это document.createDocumentFragment.
Кос,
1
@IlmariKaronen, Кос, это не вопрос безопасности или эффективности. Однако я оптимизировал его.
kolodi 04
3
@misher: OP попросил способ сохранить разрывы строк, а не способ сохранить разрывы строк и разрешить внедрение HTML. Получение бесплатной дыры в безопасности, когда вы просите помощи для исправления ошибки пользовательского интерфейса, похоже на получение бесплатной кнопки в пирожке, когда вы просите гамбургер. В любом случае, поскольку ваш "оптимизированный" код больше не кажется уязвимым, я снял свой голос против.
Ilmari Karonen
3

Вот идея, так как у вас может быть несколько символов новой строки в текстовом поле:

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

Это значение HTML сохранит новую строку. Надеюсь это поможет.

Реза
источник
1
Это также создаст сочетание неэкранированного пользовательского ввода и тегов HTML, что создаст уязвимость HTML-инъекции, если результирующая htmlстрока когда-либо будет фактически отображена как HTML.
Ilmari Karonen
@IlmariKaronen - То есть вас беспокоит то, что кто-то может напасть на себя? OP не упоминает ни одного типа базы данных. Каким образом ввод одного пользователя повлияет на другого пользователя? Возможно, это то, что предназначено только для глаз ОП и не имеет нужды в санитарии (если вообще в этом есть необходимость).
Джесси
1
@Jesse: В общем, трудно быть уверенным, что пользовательский ввод поступает из надежного источника. Даже если у злоумышленника нет возможности напрямую ввести текст в текстовое поле, было множество вирусов в социальных сетях, которые распространились, убедив наивных пользователей с помощью социальной инженерии скопировать и вставить что-то в уязвимое поле ввода. Кроме того, даже если входным данным действительно можно доверять на 100%, этот код все равно искажает любой текст, содержащий метасимволы HTML, такие как &или <. Даже если безопасность не вызывает беспокойства, это все равно ошибка.
Ilmari Karonen
@IlmariKaronen - Меня совсем не беспокоит атака, но спасибо за эту информацию!
Итан Вандер Хорн
3

Вы можете установить widthdiv с помощью Javascript и добавить, white-space:pre-wrapчтобы p tagразбить содержимое текстового поля в конце каждой строки.

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
источник
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>

tkausl
источник
2

Похожие вопросы здесь

обнаруживать разрывы строк в вводе текстовой области

обнаружить разрыв строки в текстовом поле

Вы можете попробовать это:

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


Другой вариант - использовать <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>

Abk
источник
2
Пожалуйста, перечитайте вопрос еще раз. Плакат спрашивает, как сохранить разрывы строк при размещении текста из текстового поля на странице, а не внутри другого текстового поля.
Джесси
1
Ваш первый пример содержит ошибки и не работает. (Вы даже не назначаете результат textContent.replace()для чего-либо.) Ваш второй пример уязвим для той же ошибки HTML-инъекции, что и несколько других ответов, потому что вы назначаете неэкранированный ввод пользователя innerHTML(и ваш первый пример будет одинаково уязвим, если вы попытаетесь чтобы сделать что-нибудь полезное с выводом replace()вызова).
Ilmari Karonen
Спасибо! Outlook 2013 действительно знает, что такое <pre>
user1566694