I have a 2 column layout that is destined for print. h2–5 are within the column and h1s span both columns. I'm using Paged.js for pagination.
These two pages show acceptable layouts:
But If there's a situation where a heading is at the bottom of a column, I'd like the heading to be forced to the top of next column.
If a new article starts in the bottom 40% of the page, I'd like that to be forced to the next page too.
For h2 and below I inject a div above the heading that, if the heading is at the bottom of its column, I inflate it to push into the next column.
This seems to do the job for h2–5 because they're inside a flow that's managed by the browser. But only if they're in the left column; if they're in the right, they get bumped off the page. E.g.:
Bumping H1s off the page causes issues, the shim inflates, but pushes the heading into an invisible area, or a few other strange situations.
The first image shows the heading that is present in the second image, shimmed off the page.
The following (slightly simplified) markup is what generates page 11 (right, second image)
<div class="the-articles">
<article class="architectural-review.com paper-story noted">
<p>
No question, Rem is a genius. Nonetheless, his wake is toxic: stained by
Randian egos (both triumphal and crushed), the intense interpersonal
competition, and the exploitation of intellectual and manual labour. How
does it all end, you wonder. In some ways, Tomas Koolhaas’s documentary
was a preemptive eulogy. Death is present in every shot, tugging at the
great man’s sleeve. The film is also suffused by an intense melancholy. It
is the peculiar sadness of endings: when a family line is extinguished,
when change erases beauty and meaning, when an entire world order
disintegrates.
</p>
<p>
Starchitects are still with us, even though their era is over. Koolhaas
himself called time on it in the mid-2000s. It is no contradiction to
honour them, while admitting that we must give ourselves permission to
abandon the figure of the heroic architect, and along with it the Western
blueprint for greatness that Koolhaas has so relentlessly and obsessively
perfected.
</p>
<div class="tail-meta">
From:
<span class="url"
>https://www.architectural‑review.com/essays/reputations/rem‑koolhaas‑1944/10037224.article</span
>
</div>
<!-- SHIM IS HERE -->
<div class="shim-between-articles" style="height: 181.944px;"></div>
</article>
<article id="2415273718" class="newyorker.com paper-story noted">
<h1 class="article-title" id="h2415273718">
Love Is Not a Permanent State of Enthusiasm: An Interview with Esther
Perel
</h1>
</article>
</div>
I'm doing the shim-inflation inside afterPageLayout
, it calls this function:
function identifyTooCloseToBottomHeading(
theHeading,
pageCutoffPercentage,
page,
pageFragment
) {
try {
if (theHeading !== null && theHeading !== undefined) {
const pageBox = page.element.getBoundingClientRect();
const headingBox = theHeading.getBoundingClientRect();
const pdelta = pageBox.bottom - headingBox.bottom;
const botPC = pdelta / pageBox.height; //Gives a % up the page. I.e. 100% is the top
const cutPC = pageCutoffPercentage[theHeading.tagName.toLowerCase()];
const oneRem = parseFloat(
getComputedStyle(document.documentElement).fontSize
);
if (botPC < cutPC) {
console.log("at risk", theHeading);
if (theHeading.previousElementSibling) {
AdjustShimSize(oneRem);
} else {
const thisArticle = theHeading.parentElement;
const prevArticle = thisArticle.previousElementSibling;
const shim = prevArticle.querySelector(".shim-between-articles");
const topMetaBox = thisArticle
.querySelector(".top-meta")
.getBoundingClientRect();
shim.style.height = `${
theHeading.getBoundingClientRect().height + topMetaBox.height
}px`;
}
}
}
} catch (e) {
console.log(e, theHeading);
}
function AdjustShimSize(oneRem) {
const shim = theHeading.previousElementSibling;
// calculate the height that it needs to be
try {
const shimBox = shim.getBoundingClientRect();
const container = shim.closest(".the-articles");
const containerBox = container.getBoundingClientRect();
// logShimDetails(theHeading, container, shimBox, nextElBox, containerBox, oneRem);
shim.style.height = `${containerBox.bottom - shimBox.bottom - oneRem}px`;
console.log("shimmed", theHeading);
} catch {
console.log("trouble with", theHeading);
}
}
}
This seems like it ought to work, but it's not very elegant. I suspect that there's something that I could be doing with the breaktoken
? Is there a correct way to do this?
for the H1s, I have also tried adding a class to the article above, depending on how many lines would be needed to bump the heading to the next page. I.e. a series of css rules:
.n-line-fat-bottom-20 {
margin-bottom: calc(var(--wp-line-height) * 20) !important;
}
.n-line-fat-bottom-22 {
margin-bottom: calc(var(--wp-line-height) * 24) !important;
}
and then applying the class at the same point in the Paged.js lifecycle. It has similalry unhelpful results.
If I knew ahead of time which headings would need to break, I could give them a break-before: column;
or break-before: page;
rule, but by content isn't known ahead of time. Adding these rules after the fact doesn't seem to work with the lifecycle of paged.js. I suspect that it is a lifecycle issue, as pushing the content off the page should cause a reflow of the next page if I was doing it at the right point/correct handler.