I am trying to format quotes the old-fashioned way. — Is there any way to go from this input:
<p>He argued that <q>the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm</q> and concluded that…</p>
to this output (as displayed on the browser):
He argued that “the King by his procla-
“ mation or other ways cannot change any
“ part of the common law, or statute law,
“ or the customs of the realm” and con-
cluded that…
In the example above, hyphenation, alignment and line length are set arbitrarily and for illustration purpose. They aren't of any concern.
I would like that, when a line breaks inside the <q>
, each consequent lines (that are in the <q>
), when displayed, be preceded by a quotation mark (so as the reader would visually isolate the quote). It is an old-fashioned way of formatting indirect speech.
I am open to any HTML, CSS, Vanilla JS suggestion to this. I've considered:
- Find and replace computed line breaks, or "
­
'line break'" (not the<br>
tag—I am looking for a dynamic solution: line-breaking points vary accordingly to the browser and the viewport); - Add character to each (but the first) line in a
<q>
; - Plain and simple CSS.
But I just don't know how to do the first two, and the best I could come up with so far in CSS is an unsatisfactory pseudo-workaround using <blockquote>
combined with text-shadow
:
blockquote {
position: relative;
line-height: 1em;
padding-left: .75em;
text-indent: -.75em;
overflow-y: hidden;
}
blockquote::before {
content:"“";
position: absolute;
top: 1em; left: .75em;
text-shadow:
0 1em 0 #000, 0 2em 0 #000, 0 3em 0 #000;
}
<p>He argued that <blockquote>“the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm”</blockquote> and concluded that…</p>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
It vaguely resembles what I wanted (kinda), but ultimately does not work because of the block nature of <blockquote>
, and when set inline
, the desired effect of the quotation marks being set to the left is lost. Any idea?
Cheers!
CodePudding user response:
It probably can't be done in an easy way. I tried to write a small function that might help you. The solution needs to be finalized, but you can take the basic idea. if you need a dynamic solution, use resize observer, as indicated in the example.
function quote(el, leftPad = 16) {
const txt = el.textContent.trim().split(" ");
const ctx = document.createElement("canvas").getContext("2d");
const styleList = getComputedStyle(el);
ctx.font = `${styleList.fontWeight} ${styleList.fontSize} "${styleList.fontFamily}"`;
const maxWidth = el.clientWidth;
let paragraph = [];
let line = "";
let lineWidth = 0;
let blockquote = false;
const symbolMap = new Map();
let firstLineBreak = false;
let quoteRowCount = 0;
const getWordLength = (word) => {
let wordWidth = 0;
for (let i = 0; i < word.length; i ) {
if (!symbolMap.has(word[i])) {
symbolMap.set(word[i], ctx.measureText(word[i]).width);
}
wordWidth = symbolMap.get(word[i]);
}
return wordWidth;
};
for (let i = 0; i < txt.length; i ) {
const word = txt[i] " ";
const width = getWordLength(word);
lineWidth = width;
if (txt[i].search('"') >= 0) blockquote = !blockquote;
if (lineWidth > maxWidth) {
paragraph.push(line);
if (!firstLineBreak && blockquote) {
line = '<br><span ></span>';
firstLineBreak = true;
quoteRowCount ;
} else {
if (blockquote) quoteRowCount ;
line = "";
}
lineWidth = leftPad width;
}
line = word;
}
if (line.trim() !== "") paragraph.push(line);
el.innerHTML = paragraph.join("<wbr>");
const newLineElement = el.querySelector(".newline");
if (newLineElement) newLineElement.style.height = `${quoteRowCount}em`;
}
const el = document.querySelector("p");
quote(el);
// or if you need ResizeObserver =>
//new ResizeObserver(() => quote(el)).observe(el);
p {
width: 220px;
font-size: 16px;
font-family: Arial;
padding: 0;
box-sizing: content-box;
}
.newline {
display: block;
width: 1em;
padding-right: 1em;
float: left;
overflow: hidden;
box-sizing: border-box;
word-break: break-word;
}
.newline:before {
content: '""""""""""""""""""""""""';
}
<p>
He argued that "the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm" and concluded that…
</p>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
Interesting question - how much can be done by CSS?
While I think for a totally general answer will need JS - slowly adding text to the paragraph and looking to see whether the height has changed or not - here's a snippet that does it mainly by CSS using pseudo element. However, as the browser uses the pseudo element to put in the quotation marks itself, these have to be placed in the text if they are still required.
It requires a way of getting the text that is in the paragraph before the q element into the content of the pseudo element also. Here this is done with a data attribute. Space is reserved for the column of double quotes by doing a text-indent on the paragraph.
So, not a totally general solution, but may point to a way of making it general using a bit of JS to do the content setting up initially.
p {
margin: 50px;
padding-left: 20px;
position: relative;
text-indent: -10px;
}
q {
position: relative;
}
q::before {
content: attr(data-start) '\a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " ';
position: absolute;
top: 0;
left: 0;
transform: translateX(-100%);
height: 100%;
width: auto;
z-index: -1;
text-indent: 0;
white-space: pre;
padding-left: 20px;
overflow: hidden;
}
<p>He argued that <q data-start="He argued that ">"the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm"</q> and concluded that…<br>some more text here for test</p>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>