Jak zachować podziały wierszy podczas pobierania tekstu z obszaru tekstowego?

98

Otrzymuję wartość w obszarze tekstowym, gdy użytkownik trafia do przesyłania. Następnie biorę tę wartość i umieszczam w innym miejscu na stronie. Jednak kiedy to robię, traci znaki nowej linii, co sprawia, że ​​wynik jest dość brzydki.

Oto obszar tekstowy, do którego mam dostęp:

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

Oto kod JavaScript umożliwiający dostęp do postu i jego transkrypcję.

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

Gdy dane wejściowe są takie jak:

Harmonogram grupowy:

Praktyka wtorkowa na 5. piętrze (20:00 - 23:00)

Ćwiczenia czwartkowe @ 5 piętro (20:00 - 23:00)

Niedzielne treningi @ (21:00 - 12:00)

Wynik to:

Harmonogram grupowy: Praktyki wtorkowe na 5. piętrze (20:00 - 23:00) Ćwiczenia w czwartki na piątym piętrze (20:00 - 23:00) Ćwiczenia w niedzielę @ (21:00 - 12:00)

Czy jest więc sposób na zachowanie łamania linii?

Ethan Vander Horn
źródło
1
jeśli możesz używać jQuery, może ci to pomóc: kopiuj-from-textarea-to-div-preserving -line breaks
Kevin Kloet
1
W rzeczywistości podziały wierszy są zachowane, element docelowy jest ustawiony tak, aby je ignorować podczas wyświetlania tekstu, ale jeśli go sprawdzisz, zobaczysz, że są tutaj. Odpowiedź Stefano po prostu stawia sobie za cel, aby ich nie ignorować, co jest prawdopodobnie najlepszym rozwiązaniem.
widma
2
Przepraszam, że szukam czubka, ale gdzie jest getElementByIdfunkcja od drugiego do ostatniego wiersza? Zakładam, że zdefiniowałeś to gdzie indziej lub zapomniałeś document.
trysis

Odpowiedzi:

187

Najłatwiejszym rozwiązaniem jest po prostu stylizowanie elementu, do którego wstawiany jest tekst, za pomocą następującej właściwości CSS:

white-space: pre-wrap;

Ta właściwość powoduje, że białe znaki i znaki nowej linii w pasujących elementach są traktowane w taki sam sposób, jak wewnątrz <textarea>. Oznacza to, że kolejne białe spacje nie są zwijane, a wiersze są przerywane w jawnych nowych wierszach (ale są również automatycznie zawijane, jeśli przekraczają szerokość elementu).

Biorąc pod uwagę, że kilka z opublikowanych tutaj odpowiedzi było podatnych na wstrzyknięcie HTML (np. Ponieważ przypisują one niezmienione dane wejściowe użytkownika do innerHTML) lub w inny sposób błędne, pozwól mi podać przykład, jak to zrobić bezpiecznie i poprawnie, w oparciu o oryginalny kod:


Zwróć uwagę, że podobnie jak w oryginalnym kodzie powyższy fragment używa append()i prepend(). W chwili pisania tego tekstu funkcje te są nadal uważane za eksperymentalne i nie są w pełni obsługiwane przez wszystkie przeglądarki. Jeśli chcesz być bezpieczny i zachować zgodność ze starszymi przeglądarkami, możesz łatwo je zastąpić w następujący sposób:

  • element.append(otherElement)można zastąpić element.appendChild(otherElement);
  • element.prepend(otherElement)można zastąpić element.insertBefore(otherElement, element.firstChild);
  • element.append(stringOfText)można zastąpić element.appendChild(document.createTextNode(stringOfText));
  • element.prepend(stringOfText)można zastąpić element.insertBefore(document.createTextNode(stringOfText), element.firstChild);
  • w specjalnym przypadku, jeśli elementjest pusty, oba element.append(stringOfText)i element.prepend(stringOfText)można je po prostu zastąpić element.textContent = stringOfText.

Oto ten sam fragment co powyżej, ale bez użycia append()lub prepend():


Ps. Jeśli naprawdę chcesz to zrobić bez używania white-spacewłaściwości CSS , alternatywnym rozwiązaniem byłoby jawne zastąpienie wszelkich znaków nowego wiersza w tekście <br>znacznikami HTML. Najtrudniejsze jest to, że aby uniknąć wprowadzania subtelnych błędów i potencjalnych luk w zabezpieczeniach, musisz najpierw usunąć wszelkie metaznaki HTML (co najmniej &i <) w tekście, zanim wykonasz tę zamianę.

Prawdopodobnie najprostszym i najbezpieczniejszym sposobem na zrobienie tego jest pozwolenie przeglądarce na obsługę kodu ucieczki HTML za Ciebie, na przykład:

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

Zauważ, że chociaż naprawi to podziały wierszy, nie zapobiegnie zwijaniu kolejnych białych znaków przez mechanizm renderujący HTML. Można to (w pewnym sensie) naśladować, zastępując część białych znaków w tekście nierozdzielającymi spacjami, ale szczerze mówiąc, jest to dość skomplikowane w przypadku czegoś, co można trywialnie rozwiązać za pomocą jednej linii CSS.

Ilmari Karonen
źródło
2
Wydaje się, że jest to najbardziej kompleksowa odpowiedź. Dlaczego głosowanie zostało odrzucone? Jeśli zawiera nieprawidłowe informacje, udostępnij.
Ethan Vander Horn
4
white-space: pre-line zadziałało dla mnie, ponieważ pre-wrap miał wcięcie w pierwszej linii.
dev4life
1
Dzięki stary. Zrobiłeś mój dzień
thatman
Chodzi o to, że jestem w stanie zobaczyć spacje w obszarze tekstowym, ale kiedy otrzymuję je na końcu interfejsu API i są wysyłane do wiadomości e-mail, nie zachowuje spacji.
Apurva Kunkulol,
@ApurvaKunkulol: To brzmi jak problem w kodzie po stronie serwera. Mógłbyś zadać nowe pytanie na ten temat, ale musiałbyś podać minimalny powtarzalny przykład, który demonstruje problem.
Ilmari Karonen
23

Kontener docelowy powinien mieć white-space:prestyl. Wypróbuj poniżej.

<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
źródło
10
pre-wraplub pre-linebyłoby lepiej. prezapobiega zawijaniu wierszy, więc bardzo długi wiersz tekstu wymusza przewijanie w poziomie.
Salman A
10
Ten kod jest podatny na wstrzyknięcie kodu HTML, ponieważ przypisujesz niezmienione dane wejściowe użytkownika do innerHTML. W tym przypadku, po prostu zastępując innerHTMLze textContentby go naprawić.
Ilmari Karonen
1
@IlmariKaronen To wszystko zepsuje. Musisz ustawić innerText i textContent . Tylko ustawienie textContentnie będzie działać w IE (w żadnej wersji), a innerTextsamo ustawienie może nie działać w przyszłości w przeglądarce Chrome i nie będzie działać w przeglądarce Firefox.
Ismael Miguel
3
@IsmaelMiguel: To dziwne - wysyłam to z IE 11 i tutaj działa dobrze. Najwyraźniej IE obsługuje textContentod wersji 9.
Ilmari Karonen
2
@IlmariKaronen To dziwne ... Nie byłam tego świadoma. Ale dobrym pomysłem jest dołączenie, innerTextjeśli potrzebujesz 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>

Możesz podzielić wiersze textarea na tablicę:

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

Następnie użyj go do wygenerowania być może więcej tagów p ...

kolodi
źródło
3
Co powiedział @IlmariKaronen: ten kod jest nieprawidłowy, ponieważ przypisujesz zwykły tekst ( textarea.value) do właściwości, która oczekuje HTML ( innerHTML). Jest to błąd typu, który powoduje różne problemy, od uszkodzonego tekstu po luki w zabezpieczeniach. Zamiast używać innerHTML, możesz zrobić appendChildz document.createTextNode(text)i document.createElement('br').
Kos
1
Dobrym sposobem na zwiększenie wydajności jest document.createDocumentFragment.
Kos
1
@IlmariKaronen, Kos, to nie była kwestia bezpieczeństwa ani wydajności. Jednak zoptymalizowałem to.
kolodi
3
@misher: OP poprosił o sposób na zachowanie podziałów wierszy, a nie o sposób na zachowanie podziałów wierszy i zezwolenie na wstrzykiwanie HTML. Uzyskanie darmowej dziury w zabezpieczeniach, gdy prosisz o pomoc w naprawieniu błędu interfejsu użytkownika, jest trochę jak zdobycie darmowej pinezki w paszteciku, gdy prosisz o hamburgera. W każdym razie, ponieważ twój „zoptymalizowany” kod nie wydaje się już podatny na ataki, usunąłem mój głos przeciw.
Ilmari Karonen
4

Oto pomysł, ponieważ możesz mieć wiele znaków nowej linii w polu tekstowym:

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

Ta wartość HTML zachowa znak nowej linii. Mam nadzieję że to pomoże.

reza
źródło
1
Spowoduje to również powstanie mieszanki danych wejściowych użytkownika bez zmiany znaczenia i znaczników HTML, co stworzy lukę w zabezpieczeniach umożliwiającą wstrzyknięcie HTML, jeśli wynikowy htmlciąg zostanie kiedykolwiek renderowany jako HTML.
Ilmari Karonen
@IlmariKaronen - Więc obawiasz się, że ktoś może się zaatakować? OP nie wspomina o żadnym rodzaju bazy danych. Jak dane wejściowe jednego użytkownika wpłyną w jakikolwiek sposób na innego użytkownika? Może jest to coś, co jest przeznaczone tylko dla oczu OP i nie wymaga żadnych urządzeń sanitarnych (jeśli w ogóle ich potrzebuje).
Jesse
1
@Jesse: Ogólnie rzecz biorąc, trudno mieć pewność, że dane wejściowe użytkownika pochodzą z zaufanego źródła. Nawet jeśli osoba atakująca nie ma możliwości bezpośredniego wstrzyknięcia tekstu do obszaru tekstowego, istnieje wiele wirusów mediów społecznościowych, które rozprzestrzeniły się, przekonując naiwnych użytkowników za pomocą inżynierii społecznej do skopiowania i wklejenia czegoś w wrażliwe pole wejściowe. Poza tym, nawet jeśli dane wejściowe naprawdę można zaufać w 100%, ten kod nadal zmienia dowolny tekst zawierający metaznaki HTML, takie jak &lub <. Nawet jeśli bezpieczeństwo nie jest problemem, nadal jest to błąd.
Ilmari Karonen
@IlmariKaronen - Wcale nie obawiam się ataku, ale dziękuję za te informacje!
Ethan Vander Horn
3

Możesz ustawić widthdiv za pomocą Javascript i dodać, co white-space:pre-wrapspowoduje p tagprzerwanie zawartości textarea na końcu każdej linii.

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
źródło
3

Przypuszczam, że nie chcesz, aby zawartość textarea była analizowana jako HTML. W takim przypadku możesz po prostu ustawić go jako zwykły tekst, aby przeglądarka nie traktowała go jako HTML i nie usuwała znaków nowej linii. Nie jest wymagane CSS ani przetwarzanie wstępne.

<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
źródło
2

Podobne pytania są tutaj

wykrywa podziały wierszy w polu tekstowym

wykryj łamanie linii w obszarze tekstowym

Możesz spróbować tego:

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;otrzyma zawartość tekstową obszaru tekstowego i textContent.replace(/\n/g, '<br/>')znajdzie w treści cały znak nowej linii w kodzie źródłowym /\n/gi zastąpi go znakiem końca wiersza html <br/>.


Inną opcją jest użycie <pre> tagu html . Zobacz demo poniżej

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
źródło
2
Przeczytaj ponownie pytanie. Plakat pyta, jak zachować podziały wierszy podczas umieszczania tekstu z obszaru tekstu na stronie, a nie w innym obszarze tekstu.
Jesse
1
Twój pierwszy przykład zawiera błędy i nie działa. (Nigdy nawet nie przypisujesz wyniku textContent.replace()do czegokolwiek). Twój drugi przykład jest podatny na ten sam błąd wstrzyknięcia HTML, co kilka innych odpowiedzi, ponieważ przypisujesz dane wejściowe użytkownika bez zmiany znaczenia innerHTML(a pierwszy przykład byłby równie podatny, gdybyś spróbował aby faktycznie zrobić cokolwiek pożytecznego z wynikiem replace()wywołania).
Ilmari Karonen
Dziękuję Ci! Outlook 2013 faktycznie wie, co to jest <pre>
user1566694