Forum Bugs

Pseudo elements that span multipla pages only apply to first or last page

zdenko
Hi,

I have a use-case where a ::before or ::after pseudo element is applied to a table or other such element that may be very tall where the pseudo element draws something across the full height of the original parent.

Imagine a line like revision bar or similar that is drawn using ::after pseudo selector for the whole table.

The issue I'm having is that if the ::before is used, it is only applicable to the first page of the table, and ignored on subsequent pages. Similarly for ::after where inly the end of the table on the last page is styled.

It looks like ::before does not apply after element is broken to span on next page(s) and ::after only applies for the page where the end of the element is.
mikeday
Yes that is correct, the ::before pseudo-element is treated almost exactly the same as having a first child element and ::after is like having a last child element, so by themselves they will not be repeated across pages. Perhaps you could use thead or tfoot for this? Or display: table-header-group?
zdenko
Hi Mike,

thanks for the fast response. If that is expected then I'll try to find another solution.
zdenko
I would like to revisit this. Initially I thought I can solve it by other means but then a tingle on the back of my mind was pointing me back to this .

We have implemented a revision bar / change bar in Browser purely with CSS. We had to adjust our html a bit to allow for this but it all is driven from pseudo elements.

To explain the implementation with words, the html element that needs to have revision bar is assigned a special class (class='arc-revision-element-added').

A css pseudo element (:after) is created that draws a black background that spans the height of the parent and runs all the way to the side on the left. This black background is z-indexed underneath so that it flows under the whole area of the parent and wide enough that it doesn't matter where the element is located on the x-axis.

.arc-revision-element-added:after {
    z-index: -1;
    content: "|";
    position: absolute;
    background: black;
    color: transparent;
    width: 5000px;
    height: 100%;
    left: -5000px;
    top: 0;
    bottom: 0;
}


There is some trickery on left of the element that only shows a part of the black background and this then appears as a line next to the unit that has revision. Basically the 'body' of the html is placed on a container div that is z-index elevated so that the :after can run underneath, with a dummy left container on the same z-index plane with background white that hides the unnecessary black leftover thus creating a narrow slit between left and right container that allows the black pseudo element to show through. This then creates the illusion that a black line is placed on the left of the unit for the corresponding element's height regardless where this element is placed on the x-axis.

We are currently drawing change bars in Prince with javascript, but this means that we need to run Prince twice. With a similar adapted CSS only solution, prince generates almost the same revision bars with 40-50% faster.

In Prince, instead of having the left container, we use a page rule @left-middle that hides the leftover of the revision bars that are flowing to the left.
    @left-middle {
        content: " ";
        margin-right: 1mm;
        height: 100%;
        background-color: white;
    }


Because of the limitation in Prince where pseudo element :after is not drawn on all pages if such element spans more than one page, we changed our pseudo element that drives the changebar to include all the relevant sub-elements that may be contained inside a revised element/unit like so:

.arc-revision-element-added:not(.disabled):after,
.arc-revision-element-added:not(.disabled) div[class^="arc-"]:after,
.arc-revision-element-added:not(.disabled) caption:after,
.arc-revision-element-added:not(.disabled) figcaption:after,
.arc-revision-element-added:not(.disabled) td:after,
.arc-revision-element-added:not(.disabled) li.arc-li:after,
.arc-revision-element-added:not(.disabled) p.arc-paragraph:after {
    z-index: -1;
    content: "|";
    position: absolute;
    background: black;
    color: transparent;
    width: 5000px;
    height: 100%;
    left: -5000px;
    top: 0;
    bottom: 0;
}


This works fine with the exception where a revision bar element spans more than one page in which case only the second page draws revision bar.

A simple example would be a paragraph with 4 lines that it happens to end up at the end of the current page. The first 2 lines are rendered on the first page and the next 2 lines are rendered on the second. Because of the :after pseudo-element, in Prince only the second page has revision bar on those 2 lines of the paragraph. Since paragraph is a singular element, the only way to fix it is to force the break-inside : avoid, but then this can change the layout of the PDF for existing documents.

Since this works in browser and also in Prince as long as element is not broken on multiple pages, I feel this is a bug in Prince since the pseudo element whether :before or :after is overlapping the original element but is not honoured on all pages if original element ends up being split because of unforced page breaks.

I'm attaching an example of what I'm describing, both resulting PDF and sourced html. This is just an example with minimal styles to show the behaviour.
  1. example.pdf21.7 kB
  2. result.html3.7 kB
zdenko
Hi Mike,

did you have the chance to read my previous post?
mikeday
Unfortunately the before/after pseudo-elements really are treated equivalently to first/last child elements so they will not be repeated, also absolutely positioned blocks cannot be split across multiple pages at the current time.

Perhaps there is another way of doing change bars, such as with a left border or by adding a parent element around each element? This could be done with JavaScript but in one single pass instead of two.
zdenko
I just applied a *:after rule for all elements that are within revision element and that works for most cases.
Then applied page-break: avoid for paragraphs within a revision element/unit and other elements that may be broken across 2 pages internally to prevent only half of the paragraph to have revision bar.

I cannot really just rely on border-left as some elements are more like collections with deeply nested elements that need to have revision bars. Also some elements have background or existing borders or sensitive positioning.

I guess with javascript solution, once the pages are rendered I could adjust the left border to appear at the side of the page depending on where the revision-bar element appears nested on x-axis. But then that is still a 2 pass solution. For those elements that have borders, they would require wrapping inside parent elements that have left border.

The first option works for me for now and it is just one prince pass with a bit of extra css complexity.