I try to number the verses of poems, but the result is not quite satisfactory.
The result should match the second blank example (but with number).
const pre = document.getElementById('canto');
const righe = pre.textContent.split('\n');
pre.innerHTML = '';
righe.forEach(riga => {
const span = document.createElement('span');
span.textContent = riga.trim();
pre.appendChild(span);
pre.appendChild(document.createElement('br'));
});
pre {
background-color: #f9f9f9;
padding: 2rem;
counter-reset: verso;
white-space: pre-wrap;
}
pre span {
display: block;
margin: 0;
padding: 0;
line-height: 1;
counter-increment: verso;
}
pre span:nth-child(3n+1)::before {
content: counter(verso) " ";
}
<b>With Span</b>
<pre id="canto">
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura,
ché la diritta via era smarrita.
Ahi quanto a dir qual era è cosa dura
esta selva selvaggia e aspra e forte
che nel pensier rinova la paura!
</pre>
<hr>
<b>Without Span</b>
<pre>
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura,
ché la diritta via era smarrita.
Ahi quanto a dir qual era è cosa dura
esta selva selvaggia e aspra e forte
che nel pensier rinova la paura!
</pre>
The counter()
functions counting pattern is really bonkers: 1, 4, and 7? Here's the reason:
pre span:nth-child(3n+1)::before {...
`:nth-child(3n+1)` counts every third element (`3n`) starting at one (`+1`).
Here's the heart of the following solution:
// The extracted string
text
// Split text at every line break
.split(/\r?\n|\r|\n/g)
/* .flatMap() allows us to dictate what return whatever
|| we want and we get the freedom of declaring
|| the conditions.
|| .forEach() and loops allow us that freedom but
|| .flatMap() is streamlined and simple like .map().
*/
.flatMap(line => {
/* We're using ternary operator
|| (an abbreviated "if", "else if" control)
|| If the line doesn't have any whitespace...
|| return `<q>${line}</q>`
|| but if it does return `<br>`
*/
return /[^\s]/.test(line)
?`<q>${line}</q>`
:`<br>`
});
const renderLines = (selector = "body") => {
const node = document.querySelector(selector);
if (node.tagName !== "PRE") {
node.style.whiteSpace = "pre";
}
const text = node.textContent;
const lines = text
.split(/\r?\n|\r|\n/g)
.flatMap(line => {
return /[^\s]/.test(line) ?
`<q>${line}</q>` :
`<br>`
});
node.replaceChildren();
lines.forEach(L => {
node.insertAdjacentHTML("beforeend", L);
});
};
renderLines("pre");
:root {
font: normal 400 2ch/1.5 "Philosopher", serif;
}
h1 {
font: small-caps 400 1.4rem/1.3 "Aboreto", serif;
}
pre {
font: inherit;
counter-reset: line 0;
}
q {
display: block;
counter-increment: line;
}
q::before {
content: "\A0" counter(line) "\A0\A0 ";
}
q:after {
content: "";
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Aboreto&family=Philosopher&display=swap" rel="stylesheet">
<main>
<h1>Inferno: Canto 1</h1>
<pre>
Nel mezzo del cammin di nostra vita
mi ritrovai per una selva oscura,
ché la diritta via era smarrita.
Ahi quanto a dir qual era è cosa dura
esta selva selvaggia e aspra e forte
che nel pensier rinova la paura!
</pre>
</main>