Should CSS "orphan" operate at line or block level? - html

I'm rendering HTML documents to PDF. Previously I was using Wkhtmltopdf, but it looks like Dompdf offers better support for page breaks, so I expect I will switch to that.
I'd like to apply some orphan control to my document, so I need to have a solid understanding of what orphans actually does. From W3C (source):
The orphans property specifies the minimum number of lines in a block container that must be left at the bottom of a page.
The example that is consistently offered around the web is this:
p { orphans: 3; }
This means that if there isn't space for three lines of paragraph text, a break is forced to the next page. I am rendering a document containing a lot of unordered lists, so for me it would be:
li { orphans: 2; }
However, I'd also ideally like to break an unordered list so that a bullet item block is not orphaned or widowed on its own. It would be nice to be able to do this:
ul { orphans: 2; }
That would ideally ensure that no item block could appear above or below a page break on its own. However browser/renderer support for this is patchy, and W3C above use the word "line" rather than "block". Thus, I imagine the above would just affect paragraph line control within list items, and would not affect whole list item blocks. If that is the case, is there a CSS way to do this?

As mentioned by liZe on the WeasyPrint issue tracker:
Orphans / Widows only work for line-boxes, not for block-like boxes.
Fortunately, you can define rules like:
li:last-child {
break-before: avoid;
}
li:first-child {
break-after: avoid;
}

Related

Orphan CSS: How avoid headers (h1, h2...) on bottom page?

I have a large HTML document with headers (h1, h2, h3...) and paragraphs. When I print the document, I want that, automatically, headers at bottom of document go to next page:
How can I do? I use "orphans: 3" CSS with paragraphs and works with "p" tags, but with h1 or h2 don't work.
#page {
size: A4;
}
p {
orphans:3;
}
h1, h2 {
orphans:3
}
Full example on action where:
1-2 page: paragraphs orphan works fine.
2-3 page: headers don't works.
Requirements:
HTML have one main div container.
Don't alter HTML.
Browser support isn't important (on my specific job).
I need some trick in CSS (no JS or Jquery, preferably)
I can't use page-break-before:always because I want that headers only go to next page when appears at bottom of page.
In typography an orphan is:
A paragraph-opening line that appears by itself at the bottom of a page or column, thus separated from the rest of the text.
However in HTML <h1> and <p> are different paragraphs then what you have to use is break-after property to tell layout engine to do not put a page break after that paragraph (with the side effect to move next paragraph up to previous page - if fit - or to move header to next page.
h1, h2 {
break-after: avoid-page;
}
Note about compatibility: break-after setting is a true working draft and even basic features are not widely supported (notably Internet Explorer 10 does). To workaround this you may use another property with similar meaning:
h1, h2 {
page-break-after: avoid;
}
Note that page-break-after applies to both page and columns. page-break-after isn't well supported by FF (it is a bug) then if compatibility is important and paragraph won't span across multiple pages you can workaround wrapping <h1> and <p> inside a container (let's say <section>) and then apply page-break-inside like this:
section {
page-break-inside: avoid;
}
IMO you should combine page-break-after and page-break-inside using page-break-inside with -moz prefix until it will fix that bug.

CSS: Format Based on Number of Children Elements (bonus: eliminate extra white space) [duplicate]

This question already has answers here:
Can CSS detect the number of children an element has?
(11 answers)
Closed 5 years ago.
For a project I'm working on, I recently had to wrap my head around a way to format the children of an element based on how many exist within a given element. So for example:
<nav>
<ul>
<!-- Items here may vary //-->
<li>Option</li>
<li>Option</li>
<li>Option</li>
</ul>
</nav>
Based on a script I'm running, the UL tag can have anywhere between 3 and 5 LI elements within it of varying size. However, in the layout, these all need to have the same width, and fit within a block that has a fixed width. I was also running into issues in the formatting that rendered an extra white space between my LI tags, even if I gave them the proper width.
Amount-Based Formatting
The solution I came up with may not be as advanced as some other methods I've seen, including those that perform arithmetic calculations during the render (to that end, since there aren't any calculations, it may render marginally faster, but that's beside the point). However, it strikes me as more easily read, and so for my purposes is superior.
I reasoned that some combination of complex selectors could be employed to specify exactly which elements I wanted to format. The choices were nth-child, nth-last-child, first-child, and last-child. Initially, I did nav>ul>li:nth-child(n):last-child as my selector, but ran into issues when trying to also select all sibling elements. So instead, I decided to select based on the last child element.
nav>ul>li{
width: 100%;
display: inline-block;
/* If border, padding, or margin are desirable aesthetically, then a separate
element should be put inside the LI element and formatted to include it */
margin: 0;
padding: 0;
border: 0;
}
nav>ul>li:nth-last-child(2):first-child,
nav>ul>li:nth-last-child(2):first-child ~ li {
width: 50%;
}
nav>ul>li:nth-last-child(3):first-child,
nav>ul>li:nth-last-child(3):first-child ~ li {
width: 33%;
}
nav>ul>li:nth-last-child(4):first-child,
nav>ul>li:nth-last-child(4):first-child ~ li {
width: 25%;
}
nav>ul>li:nth-last-child(5):first-child,
nav>ul>li:nth-last-child(5):first-child ~ li {
width: 20%;
}
/* Repeat ad nauseum */
In this method, the first child of N siblings is selected, and then all of its siblings are also selected.
Non-Uniform Formatting
With this methodology, you could also get clever and change the rules depending on how many siblings there are. This example sets the first and last LI elements at 20% width, and the middle one at 60% width (could be good for some layout purposes, and it's something calculations would be hard-pressed to accomplish in some cases):
nav>ul>li:nth-last-child(3):first-child,
nav>ul>li:nth-last-child(3):first-child ~ li:last-child {
width: 20%;
}
nav>ul>li:nth-last-child(3):first-child ~ li:nth-child(2) {
width: 60%;
}
Fixing the White Space Problem
I'll have to do some digging, because I actually found this solution here, and I want to give credits where it's due, but I'm having a hard time tracking it down (if a moderator who knew where it was wanted to edit it in, I'd be greatly appreciative). Basically, the gist of the problem is that inline-block elements are rendered as text, and so all elements will have at least one non-breaking space between them.
There are three solutions to this that I've seen, and each has its advantages and disadvantages.
Removing the Space
As described here, this method removes the space from between the elements. This is probably the preferred method semantically, because it relies the least on formatting tomfoolery. The drawback of this method is that the more complex the elements in question, the more obfuscated it can become. In essence, the code in question becomes this:
<nav>
<ul>
<!-- Items here may vary //-->
<li>Option</li><li>Option</li><li>Option</li>
</ul>
</nav>
Floating the Elements
As described here, this method adds two simple CSS attributes to the LI elements. It first floats all of the elements to the left, setting them to display in order, before any rendered text, and then sets them to not try to clear one another. The major drawback to this one is that any other floating elements may interact with these elements, and floated elements also tend to misbehave more frequently than others (some browsers seem to be unsure of how to handle floating elements, especially in legacy). The rule would be:
nav>ul>li {
float: left;
clear: none;
}
Hiding the Text
This is the answer I'm having a hard time finding - needless to say, it is not my idea. It is, however, the solution that I employed for my situation. Assuming we don't want to obfuscate the code or float elements, with CSS, we still have control over how that text renders (and we must remember that a non-breaking space is still text). In essence, we're shrinking the text to font-size 0. The drawback here is that we then need to make sure that the LI elements (and their children) have a proper font size. This means we need two rules:
nav>ul {
font-size: 0;
}
nav>ul>li {
/* Note, you can change this as needed, just don't delete it. */
font-size: initial;
}
You may also need to write another rule, depending on how much control you have over the LI element's contents:
nav>ul>li * {
/* Note, you can change this as needed, just don't delete it. */
font-size: initial;
}
Hope this has been helpful! :D

Is there a valid way to force a white space between list items when HTML is minified?

I am trying to make use of text-align: justify; to space the list items out. (LIKE THIS)
The problem is that my HTML is minified - which removes the spaces between the list items and ruins the text-align: justify; effect (LIKE THIS)
So usually I get around this problem by adding an char between elements - ensuring that my whitespace is preserved.
Now although this actually works for list items too (LIKE THIS - (the last demo there)) the problem here is that adding between list items is invalid HTML -
I tried the html validator - and I got this error:
One more thing: I can't use display:flex; with justify-content : space-between; because I need to suppport IE9
Any ideas?
You can add a space after each list item as generated content, but then the list items need to be laid out as inline elements, not as inline blocks, in order to make the spaces matter in justification:
.nav li {
display: inline;
}
.nav li:after {
content: " "; /* a regular space */
}
.nav li a {
display: inline-block;
}
The last rule above handles the issue that a link may contain spaces and you probably do not want them to be stretched (as they would be if the element were inline).
Original answer, written before learning the difference between inline and inline block elements in justification; retained here for reference:
No, if you have “minified” HTML code so that whitespace between elements has been removed, you cannot make the elements act as “words” in justification. Such “minification” generally changes the actual content and should not be done—it is OK to collapse inter-element whitespace to one whitespace character, but not more.
The reason is that justification algorithms recognize only whitespace in actual content. Thus, it would not help to add spaces as generated content.
So you should use a more adequate minifier, or perhaps use the settings of the one you are using, so that it does not remove inter-element whitespace.
One way to get around this is to space the li elements with margin or padding
li {
margin: 0 10px;
}

CSS List Style has random space

I am trying to code a page, and for some reason i have a random css spacing issue for my list that i created. On the bottom right i have a random space between the list and its div.
I am styling it fine i think but my code is here at jsFiddle
and it works fine there for some reason. Any ideas?
If needed i can supply the entire page link.
I want that whole entire css list to span accross the entire div but it has a huge gap between the left wall of the div and its list.
The list on the page you link to needs to have its padding (and potentially its margin ... some browsers have different default styles) cleared. Here are some rules you could use to fix this:
#navlist {
list-style-type: none; /* Removes default list style */
margin: 0;
padding: 0;
}
I highly recommend getting the Firebug extension for Firefox. It makes debugging layout issues like this very easy. It also helps you see whether the style rules you are writing are being overridden by a more specific rule elsewhere in your style sheet.
As an aside, you shouldn't be using the center element. That element has been deprecated, and should be handled via your style sheet like so: text-align: center;

How do I avoid a page break immediately after a heading

I have an HTML 4.01/CSS 2.1 document that includes an H3 heading followed by a short (one line) paragraph block and then an unordered list with several items:
<h3>Heading!</h3>
<p>Some things:</p>
<ul>
<li>Thing one</li>
<li>Thing B</li>
<li>Thing 4</li>
</ul>
My problem is that when I print the document (or render it as a PDF using wkhtmltopdf), sometimes a page break will occur right after the heading, before the paragraph, which looks quite silly.
Is there a way to stipulate that page breaks should be avoided immediately after a header? (I'm not averse to HTML5/CSS3 solutions, if that simplifies things significantly.)
Note: following suggestions, I tried using the CSS property page-break-after: avoid. This doesn't really work in any WebKit or Mozilla based browsers, though.
This is an extremely hacky solution, but it works for me:
h1 {
page-break-inside: avoid;
}
h1::after {
content: "";
display: block;
height: 100px;
margin-bottom: -100px;
}
Basically I create an invisible element that increases the size of the <h1> without affecting the content after it. When page-break-inside: avoid is applied and the whole <h1> (including the hacky element cannot fit into the page) the browser is forced to carry the <h1> to the next page.
Since the CSS property page-break-after: avoid doesn't work in any WebKit or Mozilla based browsers, use the page-break-inside: avoid over the heading and an acceptable amount of the text:
CSS
<style type="text/css">
.nobreak {
page-break-inside: avoid;
}
</style>
HTML
<div class="nobreak">
<h3>Heading!</h3>
<p>Some things:</p>
</div>
<ul>
<li>Thing one</li>
<li>Thing B</li>
<li>Thing 4</li>
</ul>
If you used HTML 5 <article> and <header>, here's a hack that seems to work with Webkit, Blink and Gecko (tweak the value 8rem to match your needs):
article > header::before
{
content: "";
display: block;
height: 8rem; /* pretend that the header is at least 8rem high so this header cannot fit near the end of page */
margin-bottom: -8rem; /* however, reduce the margin the same amount so that the following content can stay in its usual place */
page-break-inside: avoid;
break-inside: avoid;
}
This works because pseudoelement ::before is rendered downwards from top of the header and browsers do support page-break-inside: avoid; well enough to actually reserve the space at the end of the page. It also uses the fact that browsers consider the height without margins when the space required is actually measured. I don't know if this is specified in any spec or just happens to match the real world browser behavior.
Some of the other answers suggest using ::after instead but in my experience that may result in cases where the container element <article> starts to render on the previous page. Using ::before seems to work better and the start of container element also seems to move. Obviously, the difference doesn't matter if your containing element doesn't have visible edges.
Note that because you have exactly one pseudo-element ::before you might not be able to use this hack if you want to apply some other styles for ::before. This hack requires that the ::before is rendered under the other content but transparent so it cannot contain visible content.
Additional things to consider:
The page-break nor page-break-inside do not work inside tables (display:table), display:grid nor display:flex. It's still unknown if this is caused by partial browser implementation or due CSS specification actually requiring this. In practice you need to use display:block for all the parent elements up to <html> or page breaks will happen anywhere.
You cannot limit the reserved space to height of full container element. For example, if the whole <article> in the above example is less than 8rem high, the element will still skip to next page because this hack blindly reserves space for 8rem before even trying to fit the <article> on the page.
However, in practice this works better than break-after:avoid or page-break-after:avoid due real world browser support. Also, the support for widows and orphans is really poor, so you may want to apply similar hack for <p> element, too. I would suggest 2rem or 3rem space for those.
When dealing only with lines inside a Paragraph, you could use the widows and orphans attributes in CSS. But unfortunately this will not bind your header to the Paragraph or the List. This because widows and orphans are not applied on block-like elements (like headers). See Should CSS "orphan" operate at line or block level?
I tried it since I've got stuck with the same problem. It seems to work when I print the Page from the Browser (Opera in my case) but not when I export the PDF with wkhtmltopdf.
Looks like putting all the elements that we don't want to be separated into a div and style this one with page-break-inside: avoid, like suggested in the accepted answer.
In my case where I have to display some headers and tabular data, I had to build a routine that finds the headers and then counts a certain amount of table-rows ahead and relocates the header and the table(s) into one div.
I recently worked on the pdf download story which was having dynamic rows of data in table format which include various charts images(tech used=>Angular + Spring + Thymleaf + Puppeteer) Some of the key points for handling page-breaks
Try to use <div></div>blocks instead of HTML tables
Do not use display: flex on the parent container on which you want page-break-inside: avoid(use float in child element)
.child1{ float: left; }
3.If you are rendering div in loop and page-break-inside: avoid; not working You should use this CSS hack to work on a particular div
<div class="parent-container">
<div class="child1"></div>
<div class="child2"></div>
</div>
.parent-container{
position: relative;
page-break-inside: avoid;
}
.parent-container::after {
content: "";
display: block;
height: 200px;
margin-bottom: -200px;
}