Say you have this definition list (the number of "sets" is not important, just that there are more than one).
<dl>
<dt>Authors
<dd>John
<dd>Luke
<dt>Editor
<dd>Frank
</dl>
I'd like to have these definitions go horizontally. So that you'd get something like this:
Authors Editor
John Frank
Luke
And if you resized the browser so it was too narrow, it would wrap like this:
Authors
John
Luke
Editor
Frank
Is there a way to do this in a good way?
If I've understood correctly the only legal elements in a dl is the dt and dd items, so I can't wrap them in div elements or anything like that. I could split them up in separate dl lists, but that wouldn't really be correct either, as it really should be one list. Any ideas?
There's no valid tag to wrap a pair of dt and dd tags together. A di element was proposed for XHTML 2.0, but XHTML 2.0 never succeeded.
Since there's a pair of inner tags for each item, it's not like a ul, where each li tag can act as an inner wrapper for the content of each list item (in this case acting as a wrapper for each column).
Basically, either use a dl or a ul for each column. Semantic markup is nice, but without a di, a single dl isn't well equipped for this type of layout.
Edit:
As #biziclop mentioned, once CSS3 Multiple-column layout is standardized and supported, it may be possible to break the columns at the correct places (with limited options beyond that layout-wise). Eventually, the code would look something like this:
dl {column-width:100px; column-fill:auto;}
dt {break-before:always;}
Let's play with css3's not-very-well-supported multi-column feature:
Cons:
requires fixed height
won't wrap (unless you write some media queries)
http://jsfiddle.net/rCQbP/5/
dl {
-webkit-column-width: 100px;
-moz-column-width: 100px;
column-width: 100px;
/* fixed height MUST be used */
height: 200px;
}
dt ~ dt {
/* simulate column-break-before: always; */
margin-top: 1000px;
}
I solved this issue using a set of undordered list:
Please view it here: http://jsfiddle.net/radialglo/pXLB4/ Note that the width does not need to be set, but for demonstration purposes I set a large enough width so it would be pushed onto the next line. Drag the html viewport on the fiddle.
<div>
<ul>Authors
<li>John</li>
<li>Luke</li>
</ul>
<ul>
<li>Editor</li>
<li>Frank</li>
</ul>
</div>
and this styling:
div ul{
float: left;
list-style: none;
width: 100px;
margin-bottom: 1em;
}
If you're adamant about using your semantic, I'm not sure of a viable solution. This is because each definition term and its definitions aren't really grouped in a way that is easily stylable?
It's possible with some absolute positioning and nth-of-type selectors (not recognised by old browsers - yuck! - but neither is the media query, so should degrade OK): Demonstration: http://jsfiddle.net/PrJWJ/6/
dt,dd{margin:0;}
#media (min-width:20em) /* 10em per column */
{
/* Enable positioning*/
dl{position:relative;}
dt,dd{position:absolute;}
dt{top:0;} /* All terms at the top */
dd{top:1.3em;} /* 1st dd under a term at 1.3em (factoring in line spacing) */
dd+dd{top:2.6em;} /* 2nd dd under a term */
/* 1st column */
dt:nth-of-type(1),
dt:nth-of-type(1)~dd
{left:0;}
/* 2nd column */
dt:nth-of-type(2),
dt:nth-of-type(2)~dd
{left:10em;}
}
#media (max-width:19.99em)
{
/* Space before a new term */
dd+dt{margin-top:1em;}
}
However, you need to extend this according to your maximum-possible number of rows and columns.
Related
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
Here's a fantastic CSS menu:
The only disadvantage its not stretched to 100%... if it has 2 elements, it should be 50%/50%, if 4 items then 25%/25%/25%/25% just like they were table cells. How to do that? I'm new to CSS.
Use display: table/table-cell (for modern browsers and IE8+) and display-table.htc (for IE6/7).
Modify its width as 100% will make the menu span to full width.
#myfantasticmenu { width: 100%; }
I simulated the change with firebug and the needed Style defination was
#nav {
overflow: hidden; /* To clear the div */
width: 100%;
}
And about the part, where you need 50/50 for two and 25 each when the item are 4, you will require some javascript to do so.
If you consider using jQuery then it will something like
childs= $("#myfantasticmenu").children('a'); //grab the list items
childs.css('width', (100/childs.length)+%);
If avoiding scripting is your MAJOR target, then bring tables into the games, they automatically do the behavior you need.
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;
}
I have a <div> styled like a box:
.box {
border:2px solid #ccc;
background:#eee;
padding:1em;
margin:1em;
}
In this box, there could be any number of any type of child elements in any order:
<div class="box">
<h3>Howzit goin</h3>
<p>Here comes a list:</p>
<ul>
<li>I don't know what</li>
<li>this box will contain</li>
<li>but it could be anything</li>
</ul>
</div>
Most or all of the child elements inherit bottom margin of various lengths from the base typography stylesheet:
/* Example global typography */
p {margin:0 0 1.5em;}
ul {margin:0 0 2.5em;}
Which produces output like this:
...but we want to normalize the "padding" of the box so that it appears equal on all sides:
.box :last-child would be too easy, this has to work in at least IE8 as well as
modern browsers (but it could be used in conjunction with an IE-only method).
I don't want to use extra markup or javascript.
Is there any other CSS trick I can use to get the output I want?
Fiddle: http://jsfiddle.net/yuXcH/1/
As you can read in this question, even if it's about 2 years old, there's no "easy" way to do this in IE8 (the other thread is just about IE6/7, but things haven't changed - IE8 doesn't support :last-child either).
The best way, in my opinion, is to manually add a class last-child to your last child so you can do:
.box .last-child{ margin-bottom: 0; }
The alternative is using javascript, which is easier if you have a lot of boxes. With jQuery, it would just look like this:
$(function(){
$(".box :last-child").css("margin-bottom","0");
})
The only "pure CSS" solution I can think of is changing all of your padding/margins to always result in a box with same padding on all sides like Lollero suggested, but this will, compared to your previous solution, result in different margins between the elements inside of the box.
I would probably compensate the extra space by having padding or margin in both top and bottom.
http://jsfiddle.net/lollero/yuXcH/2/
Also some padding change in top and/or bottom of the parent element can be used.
http://jsfiddle.net/lollero/yuXcH/3/
Suppose we have an old-style six to eight products which enclosed in a table with borders (so called leaders or special products). Is it tabular data? Is it worth to replace this with div? If yes then how can I do this?
Thank you.
I think about the following:
These products are not correlated. And from this point of view this is NOT tabular data.
And also this table has borders which I cannot simulate with div (or maybe don't know) because of a liquid layout (width in %).
This table is on the main page. And I want to be more accessable for those devices like mobile gadgets.
So I should use div but don't know the best way. By the way, I think is it the main cause modern sites do not use borders but "blockes" with no border collapsing.
Thank you everybody.
Added: Simply put we have a table with two rows, six cells, and border=1. This is an old-scool design which I want to keep.
There are problems:
- borders
- border collapsing
I just wanna know how seasoned designers work around this. What an approach?
Sorry for so many words. And yes I know about screenshot but there is nothing special just table that has an image in each cell.
There is nothing wrong with using tables for real tables, just set up the table structure in your code and format it how you like using pure css. (Tabular data essentially must have at least two rows and columns inclusive of headings.) You only need to avoid tables when structuring your [entire] page layout.
If you want to highlight some products, by showing them as 'blocks' instead of table rows, I would put them in divs, yes.
<div class="product">some product<br /><img src="product.jpg" /></div>
<div class="product">some other product<br /><img src="product.jpg" /></div>
<div class="product">yet some other product<br /><img src="product.jpg" /></div>
CSS:
div.product {
width: 100px;
height: 100px;
float: left;
}
To 'clear' the floating of the divs, you need something like this just below the last product div.
<div style="clear: both;"></div>
Otherwise, your next element will be positioned under/over your products.
It is possible that I misunderstood (you could give a link or screenshot), but this sounds like NOT tabular data. What you are describing is a LIST of products, so it should be placed either in an <UL> unordered list or <OL> ordered list (latter if order matters a lot).
So your products would look like:
<ul id="lead-products">
<li>
Amazing Product - $10.65
...
</li>
...
</ul>
Then with CSS you could give it some style:
#lead-products { //the list
list-style: none; /* make it not display list markers */
overflow: hidden; /* used to clear up floats */
margin: 0;
}
#lead-products li { /* these are your elements/products */
margin: 5px; /* some space around it */
float: left; /* float them to the left so they are besides each other */
border: 1px solid black; /* border around the products */
width: 20%; /* dynamic width as you needed */
}