Forum How do I...?

Block elements in paragraphs footnotes

groverdill
I am using prince to lay out a document with paragraphs that contain some "chatty" footnotes, some of which contain elements like tables.

My main body paragraphs are marked up using [p] tags and my footnote tags are marked up using [span] tags, which seems to make a lot of semantic sense. However html doesn't like block-level elements like [table] to be placed in inline containers like p or span, so everything explodes as soon as it gets to the block level element in my footnote span.

The only way I can think to make this work is potentially to markup all my paragraphs as individual divs (so, use <div> instead of <p> everywhere I have a paragraph), but that feels pretty gross. Is there a better way?

I have attached some example code that demonstrates what I'm trying to do.
  1. footnote_test.html1.3 kB
    source html
  2. footnote_test.pdf38.1 kB
    generated pdf
mikeday
The other approach would be to use "display: inline-table" on nested spans instead of actual table elements, if that's any better. (Or XML which allows arbitrary element nesting, although that can introduce issues of its own).
groverdill
Thanks Mike! I've done some experiments with display: inline-table and it looks like that is going to get me where I want to go for my footnotes. There's a bit more markup and styling this way, but at least its all contained in the footnote span itself and doesn't require changes in the parent element (so I don't have to change a bunch of random p tags to divs just to format the footnote contents inside them).

Thanks again for pointing me in the right direction. I will attach the updated files in case someone wants to look at them.
  1. footnote_test_updated.html2.2 kB
    updated source html
  2. footnote_test_updated.pdf38.1 kB
    updated generated pdf
SteelWagstaff
We have run into a very similar problem with books that we're formatting as PDFs. Authors using our publishing platform would like to be able to include block elements within footnotes, but we've historically always marked up footnotes as `<span>` (i.e. inline) elements. The problem we run into is when users try to include block elements like multiple paragraphs, ordered/unordered lists, or block quotes in their footnotes. We've been using Prince since early days and know there's been a lot of development here. Does Prince now support the use of footnote content in a block element (say a <div>) rather than a span? If so, how can we safely do this?
mikeday
The tricky part is that Prince allows this via the CSS display property, but HTML syntax does not allow nesting blocks within spans and is unaware of the CSS, so it breaks the markup.

I think it would be possible to solve this with JavaScript, in which the footnote block content is elsewhere in the document and a script moves it into place, allowing a block to be inside a span without breaking everything.
SteelWagstaff
Hi Mike, thanks for your answer. In our case, our output looks something like this:
<p>I'm a sentence.<span class="footnote">I'm a footnote. <ul><li>I'm a list item</li><li>I'm another list item</li></ul><p>I'm the second paragraph in this footnote.</p></span> I'm another sentence in the original paragraph.</p>

We can't safely change the <span class="footnote"> to be a <div class="footnote"> because <p> elements can't properly contain divs (https://stackoverflow.com/a/8398003). The goal is to make the footnote container itself a block element (so that it can contain other block elements without becoming invalid HTML). Not sure what you're suggesting here, but I'm curious. Are you suggesting leaving the footnote as a span, moving it to the bottom of the page and then transforming it into a block somehow? I'm a bit lost.

In our case, the publishing platform is built on WordPress, and our footnotes are actually constructed with shortcodes, so the user input actually looks like this: [footnote]I'm a footnote. <ul><li>I'm a list item</li><li>I'm another list item</li></ul><p>I'm the second paragraph in this footnote.</p>[/footnote]. We have total control over how the shortcode is processed and turned into the span output in case that's useful to know.
markbrown
Here's an example. The footnote elements are placed outside the <p> elements so as not to break the html, and are linked by another element with class "indirect". The script moves any footnotes into their correct places.

<script>
ind = document.getElementsByClassName("indirect");
for (var i=0; i<ind.length; i++) {
    var e = document.getElementById(ind[i].getAttribute("ref"));
    if (e) ind[i].appendChild(e);
}
</script>

<p>
A paragraph containing a footnote.<span style="float: footnote;">
    <span class="indirect" ref="fn1"></span>
</span>
Content in the same paragraph.
</p>

<div id="fn1">
    <ul>
        <li>Block content in a footnote
        <li>doesn't interrupt flow
    </ul>
</div>
SteelWagstaff
Thanks Mark -- this looks very promising!
SteelWagstaff
Hi Mark & Mike --
we've spent a few days trying to get a sample implementation based on the suggestion above. It works well when a footnote occurs at the end of a paragraph, but we're still running into the same problem when the footnote occurs in the middle of a paragraph, as in the example above. As I understand it, the problem is that when the div is appended to the indirect span element inside of the paragraph, the paragraph element is forcibly closed and a new paragraph element appears to encapsulate the remainder of the text. I think the reason for this is explained fairly well here: https://stackoverflow.com/a/10763952.

So in the example above
<p>
A paragraph containing a footnote.<span style="float: footnote;">
    <span class="indirect" ref="fn1"></span>
</span>
Content in the same paragraph.
</p> 

becomes
<p>
A paragraph containing a footnote.</p><span style="float: footnote;">
    <span class="indirect" ref="fn1"><div>...</div></span>
</span>
<p>Content in the same paragraph.
</p>

We're stuck in the same place as before, frankly. The presence of a div with footnote content just simply isn't allowed by the browser inside of the p element. We're stumped and are on the verge of telling authors that they simply cannot include block elements within their footnotes, unless someone has a better idea/workaround?
markbrown
My guess is that the javascript is being run elsewhere before it gets to Prince, which is why you are getting back to the same place as before.

Can you try the following slight variation? This should ensure the script is only run once it gets to Prince.

<script>
function replaceIndirect() {
    ind = document.getElementsByClassName("indirect");
    for (var i=0; i<ind.length; i++) {
        var e = document.getElementById(ind[i].getAttribute("ref"));
        if (e) ind[i].appendChild(e);
    }
}

if (typeof Prince != "undefined") {
    replaceIndirect();
}
</script>
...

ricardopressbooks
Thank you markbrown it was useful for me and works well!
jpavel
In case this is useful to anyone else:

I've written an Ant task (though the Java code can easily be extracted for use with other projects) that post-processes HTML to address this issue.

From the linked gist:

The motivation for this task is to allow block-level elements within footnote content for use with CSS paged media (eg. transforming HTML to PDF with Prince or Antenna House Formatter). Or more explicitly, to automatically process Pandoc-generated HTML – which can include block-level footnote content – so that it can be converted to PDF with proper footnotes.

The basic problem is this: CSS paged media footnotes are created by adding the float: footnote property to an element, at which point it will be moved out of the normal flow and replaced with a footnote call. Generally footnotes are used in the middle of text, which in turn is in a paragraph (a <p>); however, HTML does not allow <p> blocks to contain other block elements, and so without tool-specific hackery (like using Prince JavaScript to manipulate the DOM tree at processing time to move a <div> into a <span>), one cannot put block content into a footnote.

Pandoc implements footnotes by inserting a footnote call as an <a> link pointing to the actual footnote content at the end of the document.

What this task does is go through the HTML looking for those footnote call links, and augmenting (or replacing) them with a <div> containing the content taken from the link target. Since most likely these calls are contained in <p>s, it also transforms such <p> elements into <div>s of a certain class, with the assumption that CSS will style the new divs to behave as paragraphs. It can also remove backlinks from the copied footnote content (you won't need them in print if the footnote is on the same page as the call), and prune the original content from the end of the document.

(this document describes an Ant task, but the Java code can easily be extracted from the execute() method and used in other projects)


https://gist.github.com/jessepav/941f19106cd1620393ac58cc855f51cd
coryschires
This is such a bummer. I get that the vast majority of footnotes do not need block elements. But, certainly, some do. This is a real use case and, imo, Prince should provide a more elegant solution.

I think we would all agree the solutions suggested on this thread, while workable, are rather complicated.

---

If you're looking for a simpler but less robust solution, here's another option... I was able to support multiple-paragraph footnotes by inserting line breaks in place of `p` elements. Visually, this achieves the goal of having multiple paragraphs while avoiding the need for JS or other significant changes. But – importantly – this will only help with paragraphs, not other block-level elements such as tables, blockquotes, etc. If you need support for other block-level elements, you'll need to try one of the other solutions described above.

mikeday
Given the constraints of HTML, block content cannot be nested inside paragraphs, so a footnote will need to be an inline reference to content outside the paragraph when is then resolved either with JavaScript as in the examples above or some kind of CSS mechanism not yet defined.

Alternatively, XHTML does allow nesting block content inside paragraphs, so that's a solution that has been waiting in the wings for over twenty years now! :D