Aby pojedyncze nowe linie w źródle traktowano jako spacje, a puste linie rozpoczynały akapity, należy najpierw utworzyć strukturę akapitów, a dopiero potem zastosować normalne zawijanie tekstu. Na Macu można to przetestować w lokalnej stronie przeglądarki; sam CSS nie robi tego rozróżnienia.
Ten przewodnik jest dla osoby przygotowującej angielską prozę do prostej strony internetowej lub podglądu Markdown. Zadaniem jest zachowanie czytelnych linii źródłowych przy jednoczesnym uzyskaniu płynnych akapitów, które się zawijają po zmianie szerokości okna przeglądarki.
Dlaczego ustawienie CSS whitespace nie rozwiązuje obu wymagań
Rozważmy ten syntetyczny tekst źródłowy. Każdy akapit zajmuje dwie linie źródłowe, a pusta linia oddziela akapity:
Panel A introduces the layout
and describes the first section.
Panel B introduces another section
and describes the remaining space.
Zamierzony efekt to dwa akapity. Wewnątrz każdego akapitu nowa linia źródłowa staje się spacją. Przeglądarka następnie dobiera widoczne podziały linii zgodnie z dostępną szerokością.
Z white-space: normal nowe linie źródłowe zwijają się razem z inną zwijalną białością. Dotyczy to też kolejnych nowych linii: pusta linia źródłowa nie tworzy akapitu. Z white-space: pre-line znaki nowej linii pozostają podziałami linii, w tym pojedynczymi nowymi liniami, które chciałeś połączyć. MDN dokumentuje te zachowania w swoim odniesieniu do white-space.
| Zastosowane ustawienie do jednego bloku tekstu | Pojedyncza nowa linia źródłowa | Pusta linia źródłowa | Odpowiednie dla tego zadania? |
|---|---|---|---|
white-space: normal |
Zwija się z białością | Również się zwija | Tylko po utworzeniu akapitów |
white-space: pre-line |
Zachowuje podział linii | Zachowuje podziały | Nie |
white-space: pre-wrap |
Zachowuje podział linii | Zachowuje podziały | Nie |
Oddzielne elementy akapitu z white-space: normal |
Pływa wewnątrz każdego akapitu | Reprezentowane przez granicę akapitu | Tak |
W3C opisuje przetwarzanie białości jako operację renderowania, która nie zmienia bazowych danych dokumentu. Jego reguły segment-break również zależą od otaczającego tekstu; zastąpienie nowej linią spacją jest odpowiednie dla angielskiej prozy użytej tutaj, a nie uniwersalną transformacją dla całego tekstu. Zobacz CSS Text Module Level 3.
Praktyczne rozwiązanie ma więc dwa etapy: rozpoznawanie akapitów w źródle, a potem pozwolenie przeglądarce na układanie ich tekstu. Standard HTML WHATWG definiuje element p jako reprezentujący akapit. Jego widoczne oddzielenie można kontrolować niezależnie przez stylowanie. Zobacz element p.
Wybierz ścieżkę pasującą do Twojego źródła
Użyj poniższej pomocy decyzyjnej przed zmianą czegokolwiek. Oddziela ona interpretację źródła od wyglądu, co pomaga uniknąć naprawiania złej warstwy.
| Co masz | Co zrobić |
|---|---|
| Zwykły tekst zawierający tylko prozę | Podziel na puste linie, połącz pojedyncze nowe linie wewnątrz każdego akapitu i utwórz elementy akapitu |
| Markdown z nagłówkami, listami lub blokami kodu | Użyj parsera Markdown i sprawdź jego zachowanie soft-break |
| Istniejący HTML z oddzielnymi elementami akapitu | Sprawdź style białości i odstępów akapitów |
| Strona zawierająca jawne elementy line-break | Popraw źródło lub konfigurację renderowania, jeśli te podziały są niezamierzone |
| Stronę, którą możesz tylko obejrzeć | Użyj dostępnego źródła lub przepływu eksportu; przykłady tutaj wymagają kontroli nad dokumentem |
Nie zastępuj każdej nowej linii spacją przed zidentyfikowaniem akapitów: to usuwa separatory potrzebne do ich odróżnienia. Podobnie nie podstawiaj powtarzanych elementów br za strukturę akapitu. WHATWG definiuje br jako podział linii i mówi, że nie może być używany do oddzielania grup tematycznych w obrębie akapitu. Zobacz element br.
Utwórz działający przykład lokalny z TextEdit
Poniższa kompletna strona wymaga TextEdit i przeglądarki z włączonym JavaScript. Zawiera edytowalne pole źródłowe i osobny podgląd. Nie używa zewnętrznych bibliotek.
- Otwórz TextEdit i wybierz
Plik > Nowy, potemFormat > Uzwógnij zwykły tekst. - Wklej kompletny przykład poniżej.
- Wybierz
Plik > Zapiszi nazwij plikparagraph-demo.html. - Jeśli zostaniesz zapytany o rozszerzenie, wybierz
Użyj .html. - Otwórz zapisany plik w Safari lub innej przeglądarce desktopowej.
Apple dokumentuje tworzenie zwykłego tekstu i kroki z rozszerzeniem w Praca z dokumentami HTML w TextEdit na Macu. Przy ponownym otwieraniu pliku do edycji użyj ustawienia TextEdit Wyświetlaj pliki HTML jako kod HTML zamiast sformatowanego tekstu w TextEdit > Ustawienia > Otwieranie i zapisywanie.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Paragraph flow demo</title>
<style>
body {
max-width: 46rem;
margin: 2rem auto;
padding: 0 1rem;
font: 1rem/1.6 system-ui, sans-serif;
}
label { display: block; }
textarea {
box-sizing: border-box;
width: 100%;
min-height: 12rem;
font: 1rem/1.5 monospace;
}
#preview p {
white-space: normal;
margin: 0;
}
#preview p + p { margin-top: 1em; }
</style>
</head>
<body>
<h1>Paragraph flow demo</h1>
<label for="source">Source text</label>
<textarea id="source">Panel A introduces the layout
and describes the first section.
Panel B introduces another section
and describes the remaining space.</textarea>
<h2>Preview</h2>
<div id="preview"></div>
<script>
function toParagraphs(source) {
return source.replace(/\r\n?/g, '\n')
.split(/\n[ \t]*\n(?:[ \t]*\n)*/)
.map(block => block
.replace(/^[ \t\n]+|[ \t\n]+$/g, '')
.replace(/[ \t]*\n[ \t]*/g, ' '))
.filter(text => text.length > 0);
}
const source = document.getElementById('source');
const preview = document.getElementById('preview');
function render() {
const paragraphs = toParagraphs(source.value).map(text => {
const paragraph = document.createElement('p');
paragraph.textContent = text;
return paragraph;
});
preview.replaceChildren(...paragraphs);
}
source.addEventListener('input', render);
render();
</script>
</body>
</html>
Konwerter najpierw normalizuje końce linii carriage-return i CRLF na LF. Następnie dzieli tekst tam, gdzie linia jest pusta lub zawiera tylko zwykłe spacje i tabulatory. W każdym powstałym bloku zastępuje pozostałą nową linię, plus sąsiednie spacje lub tabulatory, jednym zwykłym spacją.
To są jawne reguły dla tego przykładu, nie ogólny parser dokumentów. Kilka pustych linii tworzy jedną granicę akapitu.iodąc i końcowa zwykła białość jest usuwana. Puste wejście nie produkuje akapitów. Oryginalne źródło pozostaje widoczne w polu wejściowym, więc transformację można porównać z nim.
Każdy wyjściowy akapit otrzymuje swój tekst przez textContent. MDN dokumentuje, że przypisanie tej właściwości zastępuje dzieci węzła węzłem tekstowym. Tutaj jest ono przypisywane tylko do nowo utworzonych, pustych akapitów. Zobacz Node: właściwość textContent.
Funkcja konwersji przeszła sześć deterministycznych testów w Node.js: zwykłe wejście akapitu, wejście CRLF, wejście carriage-return, wejście tylko puste, powtarzane puste linie i zachowanie niełamliwej spacji. Renderowanie w przeglądarce nie było wykonywane podczas przygotowania; użyj wizualnych kontrolek poniżej, aby zweryfikować to na swoim Macu.
Sprawdzaj granice akapitów i zawijanie osobno
Zacznij od dostarczonego przykładu. Podgląd powinien zawierać Panel A introduces the layout and describes the first section. jako jeden akapit, a następnie odpowiadający akapit Panel B. Przy wąskiej szerokości okna, każdy akapit może zajmować kilka widocznych linii.
Teraz wykonaj te kontrolowane edycje:
- Zastąp pojedynczą nową linię po
layoutspacją. Struktura akapitów podglądu powinna pozostać taka sama. - Przywróć tę nową linię i usuń pustą linię między dwoma blokami. Podgląd powinien stać się jednym akapitem.
- Dodaj pustą linię zawierającą tylko spacje lub tabulatory między blokami. Podgląd powinien wrócić do dwóch akapitów.
- Dodaj tam kilka pustych linii. Przerwa powinna pozostać jedną przerwą akapitu.
- Zmień rozmiar okna przeglądarki. Granica akapitu powinna pozostać stała, podczas gdy linie wewnątrz każdego akapitu się zawijają.
Ta sekwencja odróżnia granice źródłowe od zawijania przeglądarki. W przykładzie odstępy akapitów pochodzą z reguły marginesu sąsiednich akapitów. Zmiana tego marginesu zmienia widoczną przerwę bez zmiany sposobu parsowania źródła.
Jeśli użyjesz white-space: normal bez tego konwertera, wizualne połączenie nie oznacza, że nowa linia źródłowa została usunięta. MDN wyjaśnia, że zwykła białość treści tekstowej pozostaje w DOM nawet wtedy, gdy CSS ją zwija podczas renderowania. Zobacz Obsługa białości.
Użyj parsowania Markdown dla dokumentów Markdown
Markdown już dostarcza konwencję akapitu potrzebną tutaj. CommonMark definiuje akapit jako kolejne linie niepuste, które nie są innym typem bloku; puste linie oddzielają akapity. Zwykła nowa linia wewnątrz akapitu to soft break, który renderer może wyprowadzić jako nową linię lub spację. Zobacz akapity CommonMark i miękkie podziały linii.
Wklej syntetyczny przykład do widoku źródłowego Twojego edytora Markdown i zbadaj jego podgląd. Jeśli każda pojedyncza nowa linia produkuje widoczny podział, sprawdź udokumentowane opcje renderowania edytora. Nie zakładaj, że każda aplikacja Markdown udostępnia to samo ustawienie.
Dla projektu używającego nazwaną bibliotekę markdown-it, jej udokumentowana opcja breaks domyślnie wynosi false. Ustawienie jej na true zamienia nowe linie akapitów na elementy br. Zachowanie jej na false wspiera wynik płynnych akapitów, o ile wyjściowy CSS również zwija białość. Zobacz MarkdownItOptions.
Jawne twarde podziały Markdown nadal wymagają uwagi. Według CommonMark, dwa lub więcej końcowych spacji lub końcowy ukośnik odwrotny mogą stworzyć twardy podział linii. Usuń te markery tylko tam, gdzie są niezamierzone. Reguły są udokumentowane w twardych podziałach linii.
Nie uruchamiaj konwertera zwykłego tekstu na całym dokumencie Markdown. On nie rozpoznaje nagłówków, list, ogrodzonych kodów ani reguł wcięć. Niech parser Markdown zachowa te struktury; zastosuj stylowanie białości do wynikowych akapitów prozy.
Zachowuj granice przeglądarki i kształtowania tekstu
Trzymaj każdy akapit prozy jako ciągły tekst, gdzie to praktyczne. Przykład tworzy jeden węzeł tekstowy na akapit i zostawia zawijanie linii i układ glifów przeglądarce. Nie mierzy słów ani nie wstawia osobnego elementu wokół każdego znaku.
Ta granica ma znaczenie dla typografii. W3C określa okoliczności, w których kształtowanie musi kontynuować się przez granice elementów inline i okoliczności, w których musi się przerywać, w tym pewne zmiany odstępów i wyrównania. Unikaj dodawania niepotrzebnych pudełek inline tylko po to, by kontrolować pozycje linii. Zobacz Kształtowanie przez granice elementów.
Używaj konwertera tylko wtedy, gdy każda izolowana nowa linia źródłowa jest edycyjnym zawijaniem. Jeśli nowa linia celowo dzieli etykiety lub wpada w słowo, łączenie jej spacją zmienia treść. Przykład traktuje też linię zawierającą niełamliwą spację jako treść, a nie pusty separator; MDN odróżnia niełamliwe spacje od zwykłej zwijalnej białości dokumentu w Obsłudze białości.
Dla angielskiej prozy, ustal granice akapitów najpierw i pozwól na normalne zawijanie przeglądarki wewnątrz nich. Lokalny przykład sprawia, że ta reguła jest inspekcyjna: źródło zachowuje swoje edytowalne linie, podgląd zawiera prawdziwe akapity, a zmiana rozmiaru zmienia tylko widoczne zawijanie linii.
Źródła
- CSS Text Module Level 3 — W3C.
- HTML Standard: The p element — WHATWG.
- HTML Standard, Edition for Web Developers: The br element — WHATWG.
- white-space CSS property — MDN Web Docs.
- Handling whitespace — MDN Web Docs.
- Node: textContent property — MDN Web Docs.
- Work with HTML documents in TextEdit on Mac — Apple Support.
- CommonMark Spec, Version 0.31.2 — CommonMark.
- MarkdownItOptions — markdown-it.