I am trying to nest tables within a table row, while maintaining the appearance of a single table, as shown in the example below (a table with a single row with a data value and two nested tables, one 2x2 and the other 3x3) :
This is just an example; the actual table has significantly more rows and columns. I want to use tables because of the natural reflowing of column widths and row heights to fit the table data without having to worry about the container size (i.e. table width = 100%).
The problem I am having is that the tallest table sets the row height, but the other tables don't expand to fill that height, so the internal borders don't stretch from top to bottom as shown in the result of this snippet:
.display {
border-collapse: collapse;
}
.display, .display td, .display th {
border: 1px solid black;
}
.subtable {
border-collapse: collapse;
}
.subtable td {
border-top: 0;
border-bottom: 1px solid black;
border-left: 0;
border-right: 1px solid black;
}
.subtable tr td:last-of-type {
border-top: 0;
border-bottom: 1px solid black;
border-left: 0;
border-right: 0;
}
.subtable tr:last-of-type td {
border-top: 0;
border-bottom: 0;
border-left: 0;
border-right: 1px solid black;
}
.subtable tr:last-of-type td:last-of-type {
border: 0;
}
td {
padding: 5px;
}
td.d-subtable {
padding: 0;
}
<table class="display" cellpadding="0">
<tr><th>Customer</th><th>Items</th><th>Payments</th></tr>
<tr><td>Customer Name</td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>Item1</td><td>5</td><td>$400.00</td></tr><tr><td>Item2</td><td>10</td><td>$200.00</td></tr><tr><td>Item3</td><td>2</td><td>$500.00</td></tr></table></td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>12 Sep 2018</td><td>$3,000.00</td></tr><tr><td>18 Sep 2018</td><td>$2,000.00</td></tr></table></td>
</tr>
</table>
Now I know I can resolve this problem using rowspan (and that is how I am currently solving the problem) but that requires deciding in advance which rows to line up and that can lead to issues such as that generated by the below snippet, where it would clearly be better if I'd applied rowspan="2" to the first row (instead of the last row) of the table with 2 rows:
td {
border: 1px solid black;
}
table {
border-collapse: collapse;
width: 500px;
}
<table cellpadding="5">
<tr><td rowspan="3">x</td>
<td>problem when you have some really long text in the first row</td><td>p</td><td>a</td><td>b</td><td>c</td></tr><tr><td rowspan="2">z</td><td rowspan="2">q</td><td>d</td><td>e</td><td>f</td></tr><tr><td>g</td><td>some other really long text</td><td>i</td>
</tr>
</table>
I would prefer the above table to look like this:
Is there a way to achieve what I want using HTML/CSS? There are a lot of rows in the table so I'd prefer the browser sort it out before rendering. However if it's not possible I'm open to a Javascript/JQuery solution.
Update
Although I did find a workable solution at the time (see my posted answer) I have since encountered some situations where setting the widths of the columns in advance (even as percentages) was difficult owing to not being able to anticipate all the possible data to be displayed. So I'm hoping to find an answer that doesn't rely on doing that.
Since I didn't make it as clear as I should have, I have multiple rows in which I want to nest tables, keeping the heights matched as well as the column widths. For example, for two rows, I would like to be able to create a layout like this:
Where with raw table HTML the result looks like this:
.display {
border-collapse: collapse;
}
.display, .display td, .display th {
border: 1px solid black;
}
.subtable {
border-collapse: collapse;
}
.subtable td {
border-top: 0;
border-bottom: 1px solid black;
border-left: 0;
border-right: 1px solid black;
}
.subtable tr td:last-of-type {
border-top: 0;
border-bottom: 1px solid black;
border-left: 0;
border-right: 0;
}
.subtable tr:last-of-type td {
border-top: 0;
border-bottom: 0;
border-left: 0;
border-right: 1px solid black;
}
.subtable tr:last-of-type td:last-of-type {
border: 0;
}
td {
padding: 5px;
}
td.d-subtable {
padding: 0;
}
<table class="display" cellpadding="0">
<tr><th>Customer</th><th>Items</th><th>Payments</th></tr>
<tr><td>Customer 1</td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>Item1</td><td>5</td><td>$400.00</td></tr><tr><td>Item2</td><td>100</td><td>$20.00</td></tr><tr><td>Item3</td><td>2</td><td>$500.00</td></tr></table></td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>12 Sep 2018</td><td>$3,000.00</td></tr><tr><td>18 Sep 2018</td><td>$2,000.00</td></tr></table></td>
</tr>
<tr><td>Customer 304</td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>Item4</td><td>5</td><td>$6.00</td></tr></table></td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td>20 Sep 2018</td><td>$4.00</td></tr><tr><td>27 Sep 2018</td><td>$26.00</td></tr></table></td>
</tr>
</table>
I would suggest the use of flex to achieve your need. Flex is very powerful for this kind of layout as we can easily let the content guide it. Please, see the attached snippet. It is purely made from HTML and CSS. No fixed sizes and no Javascript required.
(old snippet)
.outer {
display: flex;
flex-wrap: wrap;
/* For demo purposes */
max-width: 500px;
margin: 20px auto;
border-left: 1px solid black;
border-top: 1px solid black;
}
.column {
flex: 1 1 auto;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.row {
display: flex;
flex-direction: row;
flex: 1 1 auto;
}
.inner {
flex: 1 1 auto;
display: flex;
flex-direction: column;
}
.item {
border-right: 1px solid black;
border-bottom: 1px solid black;
text-align: center;
padding: 3px;
}
.item.heading {
font-weight: bold;
}
.item:not(.heading) {
flex: 1 1 auto;
justify-content: center;
align-items: center;
display: flex;
}
.fixed .narrow {
flex-basis: 20px;
}
<div class="outer">
<div class="column">
<div class="item heading">Customer</div>
<div class="item">
<span>Customer Name</span>
</div>
</div>
<div class="column">
<div class="item heading">Items</div>
<div class="inner fixed">
<div class="row">
<div class="item">Item1</div>
<div class="item narrow">5</div>
<div class="item last">$400.00</div>
</div>
<div class="row">
<div class="item">Item2</div>
<div class="item narrow">10</div>
<div class="item last">$200.00</div>
</div>
<div class="row">
<div class="item">Item3</div>
<div class="item narrow">2</div>
<div class="item last">$500.00</div>
</div>
</div>
</div>
<div class="column">
<div class="item heading">Payments</div>
<div class="inner">
<div class="row">
<div class="item">12 sep 2018</div>
<div class="item">$3,000.00</div>
</div>
<div class="row">
<div class="item">
18 sep 2018
</div>
<div class="item">
$2,000.00
</div>
</div>
</div>
</div>
</div>
Update: Changed according to your comment/answer. It somewhat depends on your HTML structure. I had to move the headings to its own ".row.row-item" and therefor needed to set a flex-basis to align the columns. This can be extended with multiple ".row.row-item". See snippet below.
.outer {
display: flex;
flex-wrap: wrap;
/* For demo purposes */
max-width: 600px;
margin: 20px auto;
border-left: 1px solid black;
border-top: 1px solid black;
}
.column {
flex: 1 1 33.33%;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.row {
display: flex;
flex-direction: row;
flex: 1 1 auto;
}
.row-item {
flex-basis: 100%;
}
.inner {
flex: 1 1 auto;
display: flex;
flex-direction: column;
}
.item {
border-right: 1px solid black;
border-bottom: 1px solid black;
text-align: center;
}
.item.heading {
font-weight: bold;
flex: 1 1 33.33%;
}
.item:not(.heading) {
flex: 1 1 33.33%;
justify-content: center;
align-items: center;
display: flex;
}
.fixed .narrow {
flex: 1 1 20px;
}
<div class="outer">
<div class="row row-item">
<div class="item heading">Customer</div>
<div class="item heading">Items</div>
<div class="item heading">Payments</div>
</div>
<div class="row row-item">
<div class="column">
<div class="item">
<span>Customer 1</span>
</div>
</div>
<div class="column">
<div class="inner fixed">
<div class="row">
<div class="item">Item1</div>
<div class="item narrow">5</div>
<div class="item last">$400.00</div>
</div>
<div class="row">
<div class="item">Item2</div>
<div class="item narrow">10</div>
<div class="item last">$200.00</div>
</div>
<div class="row">
<div class="item">Item3</div>
<div class="item narrow">2</div>
<div class="item last">$500.00</div>
</div>
</div>
</div>
<div class="column">
<div class="inner">
<div class="row">
<div class="item">12 sep 2018</div>
<div class="item">$3,000.00</div>
</div>
<div class="row">
<div class="item">
18 sep 2018
</div>
<div class="item">
$2,000.00
</div>
</div>
</div>
</div>
</div>
<div class="row row-item">
<div class="column">
<div class="item">
<span>Customer 304</span>
</div>
</div>
<div class="column">
<div class="inner fixed">
<div class="row">
<div class="item">Item4</div>
<div class="item narrow">5</div>
<div class="item last">$6.00</div>
</div>
</div>
</div>
<div class="column">
<div class="inner">
<div class="row">
<div class="item">20 sep 2018</div>
<div class="item">$4.00</div>
</div>
<div class="row">
<div class="item">
27 sep 2018
</div>
<div class="item">
$26.00
</div>
</div>
</div>
</div>
</div>
<div class="row row-item">
<div class="column">
<div class="item">
<span>Customer 605</span>
</div>
</div>
<div class="column">
<div class="inner fixed">
<div class="row">
<div class="item">Item5</div>
<div class="item narrow">50</div>
<div class="item last">$60.00</div>
</div>
<div class="row">
<div class="item">Item6</div>
<div class="item narrow">3</div>
<div class="item last">$260.00</div>
</div>
</div>
</div>
<div class="column">
<div class="inner">
<div class="row">
<div class="item">29 sep 2018</div>
<div class="item">$40.00</div>
</div>
<div class="row">
<div class="item">
30 sep 2018
</div>
<div class="item">
$206.00
</div>
</div>
</div>
</div>
</div>
</div>
I was eventually able to solve this by writing an onload function to find the maximum height of all the nested tables in a given row and then set the height of every nested table in that row to the same value.
window.onload=function () {
let rows = document.querySelectorAll('tr');
for (let r = 0; r < rows.length; r++) {
let subtables = rows[r].querySelectorAll('.subtable');
let maxHeight = 0;
for (let i = 0; i < subtables.length; i++) {
maxHeight = Math.max(maxHeight, subtables[i].clientHeight);
}
for (let i = 0; i < subtables.length; i++) subtables[i].style.height='' + maxHeight + 'px';
}
};
The only downside of this solution was that it meant I had to assign widths to the <td>s in the nested tables. However since I could use percentage widths this wasn't too big an issue for me:
<table class="display" cellpadding="0">
<tr><th>Customer</th><th>Items</th><th>Payments</th></tr>
<tr><td>Customer 1</td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td width="35%">Item1</td><td width="20%">5</td><td width="45%">$400.00</td></tr><tr><td>Item2</td><td>10</td><td>$200.00</td></tr><tr><td>Item3</td><td>2</td><td>$500.00</td></tr></table></td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td width="60%">12 Sep 2018</td><td width="40%">$3,000.00</td></tr><tr><td>18 Sep 2018</td><td>$2,000.00</td></tr></table></td>
</tr>
<tr><td>Customer 2</td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td width="35%">Item4</td><td width="20%">5</td><td width="45%">$600.00</td></tr></table></td>
<td class="d-subtable"><table class="subtable" cellpadding="0"><tr><td width="60%">20 Sep 2018</td><td width="40%">$4,000.00</td></tr><tr><td>27 Sep 2018</td><td>$2,000.00</td></tr></table></td>
</tr>
</table>
Final output:
This example is a responsive design which utilizes only HTML and nested CSS Grids inside of a Flexbox.
I took the liberty of adding headers and a pagination placeholder to your scenario. These may be removed without affecting the rest of the layout.
I have created a GitHub repo nested-CSS-Grid-and-Flexbox-Playground which contains an Angular application utilizing this layout with dynamic data as well as references I accumulated while researching this project.
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div class="container">
<div class="header">
<h1>Customers List</h1>
</div>
<div class="list">
<div class="customer-column-header">Customers</div>
<div class="invoice-column-header">Invoices</div>
<div class="payments-column-header">Payments</div>
<div class="customer-row">
<div class="customer-column">
<div>Acme Widget Manufacturing, Inc.</div>
</div>
<ul class="invoice-column">
<li class="invoice-row-header">
<div>Description</div>
<div>Quantity</div>
<div>Price</div>
</li>
<li class="invoice-row">
<div>Item 1</div>
<div>5</div>
<div>$400.00</div>
</li>
<li class="invoice-row">
<div>Item 2</div>
<div>10</div>
<div>$200.00</div>
</li>
<li class="invoice-row">
<div>Item 3</div>
<div>2</div>
<div>$500.00</div>
</li>
</ul>
<ul class="payment-column">
<li class="payment-row-header">
<div>Date</div>
<div>Amount</div>
</li>
<li class="payment-row">
<div>12 Sep 2018</div>
<div>$3,000.00</div>
</li>
<li class="payment-row">
<div>18 Sep 2018</div>
<div>$2,000.00</div>
</li>
<li class="payment-row">
<div>12 Sep 2018</div>
<div>$3,000.00</div>
</li>
<li class="payment-row">
<div>18 Sep 2018</div>
<div>$2,000.00</div>
</li>
</ul>
</div>
<div class="customer-row">
<div class="customer-column">
<div>Beta Company</div>
</div>
<ul class="invoice-column">
<li class="invoice-row-header">
<div>Description</div>
<div>Quantity</div>
<div>Price</div>
</li>
<li class="invoice-row">
<div>Item 1</div>
<div>5</div>
<div>$400.00</div>
</li>
<li class="invoice-row">
<div>Item 2</div>
<div>10</div>
<div>$200.00</div>
</li>
<li class="invoice-row">
<div>Item 3</div>
<div>2</div>
<div>$500.00</div>
</li>
</ul>
<ul class="payment-column">
<li class="payment-row-header">
<div>Date</div>
<div>Amount</div>
</li>
<li class="payment-row">
<div>12 Sep 2018</div>
<div>$3,000.00</div>
</li>
<li class="payment-row">
<div>18 Sep 2018</div>
<div>$2,000.00</div>
</li>
</ul>
</div>
</div>
<div class="pagination">
<p>Pagination Placeholder</p>
</div>
</div>
</body>
</html>
CSS
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: inherit;
}
ul {
list-style-type: none;
}
.container {
width: 62.5rem;
max-width: 80rem;
margin: 0 auto;
background-color: lightgray;
display: flex;
flex-direction: column;
justify-content: center;
}
.header {
padding: .5rem;
background-color: darkgrey;
align-self: center;
}
.list {
background-color: darkcyan;
display: grid;
grid-template-columns: 1fr repeat(2, minmax(min-content, 1fr));
}
.customer-column-header {
grid-column: 1 / 2;
}
.invoice-column-header {
grid-column: 2 / 3;
}
.payments-column-header {
grid-column: 3 / 4;
}
.customer-column-header,
.invoice-column-header,
.payments-column-header {
padding: .5rem;
text-align: center;
}
.customer-row {
border-left: 1px solid;
border-top: 1px solid;
background-color: orangered;
grid-column: 1 / -1;
display: grid;
grid-template-columns: 1fr repeat(2, minmax(min-content, 1fr));
}
.customer-column {
grid-column: 1 / 2;
}
.invoice-column {
grid-column: 2 / 3;
}
.payment-column {
grid-column: 3 / 4;
}
.customer-column {
align-self: center;
justify-self: right;
}
.customer-column > div {
padding: 1rem;
}
.invoice-column {
border-left: 1px solid;
}
.invoice-row-header {
background-color: darkcyan;
border-bottom: 1px solid;
display: grid;
grid-template-columns: repeat(3, minmax(6rem, 1fr));
justify-self: stretch;
}
.invoice-row-header > div {
text-align: right;
padding: .5rem;
justify-self: stretch;
align-self: stretch;
}
.invoice-row-header > div:nth-child(2) {
border-left: 1px solid;
border-right: 1px solid;
}
.invoice-row {
border-bottom: 1px solid;
display: grid;
grid-template-columns: repeat(3, minmax(6rem, 1fr));
justify-items: stretch;
align-items: stretch;
}
.invoice-row > div {
text-align: right;
padding: .5rem;
justify-self: stretch;
align-self: stretch;
}
.invoice-row div:nth-child(2) {
border-left: 1px solid;
border-right: 1px solid;
}
.payment-column {
border-left: 1px solid;
}
.payment-row-header {
background-color: darkcyan;
border-bottom: 1px solid;
display: grid;
grid-template-columns: repeat(2, minmax(6rem, 1fr));
justify-self: stretch;
}
.payment-row-header > div {
text-align: right;
padding: .5rem;
justify-self: stretch;
align-self: stretch;
}
.payment-row-header > div:nth-child(1) {
border-right: 1px solid;
}
.payment-row {
border-bottom: 1px solid;
display: grid;
grid-template-columns: repeat(2, minmax(6rem, 1fr));
justify-items: stretch;
align-items: stretch;
}
.payment-row > div {
text-align: right;
padding: .5rem;
justify-self: stretch;
align-self: stretch;
}
.payment-row > div:nth-child(1) {
border-right: 1px solid;
}
.pagination {
padding: .5rem;
background-color: darkgrey;
align-self: center;
}
#media only screen and (min-width: 120rem) {
.container {
max-width: 80rem;
margin: 0;
}
}
#media only screen and (max-width: 80rem) {
.container {
max-width: 62.5rem;
margin: 0 auto;
}
}
#media only screen and (max-width: 62.5rem) {
.container {
max-width: 45rem;
margin: 0 auto;
}
.list {
grid-template-columns: repeat(autofit, 100%);
}
.customer-column-header,
.invoice-column-header,
.payments-column-header {
grid-column: 1 / -1;
}
.customer-row {
grid-template-columns: repeat(autofit, 100%);
justify-content: center;
}
.customer-row .customer-column,
.customer-row .invoice-column,
.customer-row .payments-column {
grid-column: 1 / -1;
}
.customer-column {
justify-self: center;
}
}
#media only screen and (max-width: 45rem) {
.container {
width: 100%;
margin: 0 auto;
}
}
cref:https://codepen.io/randydaddis/pen/LqEjEg
No rowspans or colspans Were Harmed Making This Table
The following is a diagram of the table sans thead:
The term "column" is termed loosely. For example: column td.items refers to all
td.items as a group.
There is the main table...
...and then a tbody for each customer row. Having multiple tbodies is valid.
Next is a tr and then...
...a td. This td is the columns: .customer, .items, and .payments.
Within each td is a sub-table and sub-tbody.
The first column td.customer is limited to one sub-row since the content for each
tbody.row is just a
customer's name. The cells for this column have the properties
that control tbody.row height
(see comments in demo).
The second column td.items can have multiple sub-rows, the demo features from 1 to 3
sub-rows.
The last column td.payments can have multiple sub-rows as well, the demo features
2 sub-rows each.
Demo
Details are commented in demo
The demo is responsive (might need MQ for edge cases and mobile devices).
Valid HTML/CSS and semantically sound.
No JavaScript/jQuery.
Built purely with <table>, <thead>, <tbody>, <tr>, <th>, and <td> tags (not even a div or span).
* { border: 0px none transparent; padding: 0px; margin: 0px; }
/*
|- table-layout: fixed;
|| Gives you more control over table behavior such as setting column widths and
|| table adhering to them.
===
|- height: 100%; width: 100%;
|| Applied to table.main as well as the nested tables. It facilitates all of
|| the nested tables to stretch evenly within tbody.row.
*/
table { border-collapse: collapse; border: 2px solid #000;
table-layout: fixed; height: 100%; width: 100%; }
table table { border: 1px solid #000; }
.main { min-height: 40vh; }
th { border-bottom: 2px solid #000; outline: 1px solid #000; }
/*
|| This rule set determines content wrapping behavior within each cell.
*/
td td { border: 1px solid #000; padding: 0px 5px; word-wrap: break-word;
word-break:break-word; overflow: hidden}
.items td:first-of-type { width: 30%; }
.items td:nth-of-type(2) { width: 25%; text-align: right; }
.items td:last-of-type { width: 45%; text-align: right; }
.payments td:last-of-type { text-align: right; }
.customer,
.items,
.payments { border: 0px none transparent; padding: 0px; }
th:first-of-type { border-left: 3px solid #000; width: 20%; }
th:nth-of-type(2) { width: 40%; }
th:last-of-type { border-right: 3px solid #000; width: 40%; }
/*
|| This allows the tr.row to always be equal in height as well as being
|| responsive. This column was the best choice as the one to stabilize height
|| since the content probably changes the least and it has the most space to use
|| hidden spacers (see next comment).
*/
.customer table { min-height: 20vh; }
/*
|| This sets a within the cells in the first column. Although vh units
|| are very responsive, they will start collapsing if the viewport height is
|| reduced disproportionately vs. viewport width. Thus, this pseudo-element
|| ensures a minimum height of 60px for both tr.row.
*/
.customer td::after { content: '\a0'; display: inline-block; width:0.5px;
min-height: 60px; line-height: 60px; vertical-align: middle; }
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
</head>
<body>
<table class='main'>
<thead>
<tr>
<th>Customer</th><th>Items</th><th>Payments</th>
</tr>
</thead>
<tbody class='row'>
<tr>
<td class='customer'>
<table><tr><td>
Customer 1
</td></tr></table>
</td>
<td class='items'>
<table>
<tr><td>Item1</td><td>5</td><td>$400.00</td></tr>
<tr><td>Item2</td><td>100</td><td>$20.00</td></tr>
<tr><td>Item3</td><td>2</td><td>$500.00</td></tr>
</table>
</td>
<td class='payments'>
<table>
<tr><td>12 Sep 2018</td><td>$3,000.00</td></tr>
<tr><td>18 Sep 2018</td><td>$2,000.00</td></tr>
</table>
</td>
</tr>
</tbody>
<tbody class='row'>
<tr>
<td class='customer'>
<table><tr><td>
Customer 304
</td></tr></table>
</td>
<td class='items'>
<table>
<tr><td>Item4</td><td>5</td><td>$6.00</td></tr>
</table>
</td>
<td class='payments'>
<table>
<tr><td>20 Sep 2018</td><td>$4.00</td></tr>
<tr><td>27 Sep 2018</td><td>$26.00</td></tr>
</table>
</td>
</tr>
</tbody>
<tbody class='row'>
<tr>
<td class='customer'>
<table><tr><td>
Customer 888
</td></tr></table>
</td>
<td class='items'>
<table>
<tr><td>Item5</td><td>50</td><td>$100.00</td></tr>
<tr><td>Item6</td><td>10</td><td>$500.00</td></tr>
</table>
</td>
<td class='payments'>
<table>
<tr><td>10 Nov 2018</td><td>$3,000.00</td></tr>
<tr><td>17 Nov 2018</td><td>$7,000.00</td></tr>
</table>
</td>
</tr>
</tbody>
</table>
</body>
</html>
References
table-layout
vh and vw
word-wrap: break-word; word-break:break-word;
:first-/:nth-/:last-of-type
::before/after
Here's a JavaScript solution that sets both the row heights and column widths automatically (does not require specifying column widths), and supports resizing.
The complete solution/demo is here: http://jsfiddle.net/ez0jqc1L/
The one caveat is this requires that you wrap the content of all <td> cells with <div class="content"></div> (see the jsfiddle for an example).
First, we fix the subtable heights:
let tbody = /* the tbody of the <table> containing subtables we are fixing */;
for(let r = 0; r < tbody.children.length; r++) {
let row = tbody.children[r];
let subtables = row.querySelectorAll('.subtable');
let maxHeight = 0;
for(let i = 0; i < subtables.length; i++) {
maxHeight = Math.max(maxHeight, subtables[i].clientHeight);
}
for(let i = 0; i < subtables.length; i++) {
subtables[i].style.height = maxHeight + 'px';
}
}
Then, we iterate over all subtable columns to compute the max width for each set of vertically adjacent cells (we store this into the maxColWidths array, each index of the array corresponds to the set of vertically adjacent cells):
let forEachSubtableColumn = function(f) {
for(let r = 0; r < tbody.children.length; r++) {
let row = tbody.children[r];
let subtables = row.querySelectorAll('.subtable');
let c = 0;
for(let i = 0; i < subtables.length; i++) {
let subtable = subtables[i];
if(subtable.children.length === 0 || subtable.children[0].children.length === 0) {
continue;
}
let stbody = subtable.children[0];
for(let j = 0; j < stbody.children.length; j++) {
let srow = stbody.children[j];
for(let k = 0; k < srow.children.length; k++) {
let td = srow.children[k];
f(c + k, td);
}
}
c += stbody.children[0].children.length;
}
}
};
let maxColWidths = [];
forEachSubtableColumn(function(c, td) {
if(c >= maxColWidths.length) {
maxColWidths.push(td.children[0].clientWidth);
} else {
maxColWidths[c] = Math.max(td.children[0].clientWidth, maxColWidths[c]);
}
});
Finally, we set the column widths. This is where the <div class="content"> wrapping is required, because setting the width of a <td> does not guarantee the browser will make it that exact width, but I can have this guarantee with an element having display: inline-block.
forEachSubtableColumn(function(c, td) {
td.children[0].style.width = maxColWidths[c] + 'px';
});
For resizing support we clear all the forced widths and heights so we can recompute them afterwards:
onresize = function() {
let tables = document.querySelectorAll('.subtable');
for(let i = 0; i < tables.length; i++) {
let table = tables[i];
table.style.removeProperty('height');
}
let tds = document.querySelectorAll('.subtable td');
for(let i = 0; i < tds.length; i++) {
let td = tds[i];
td.children[0].style.removeProperty('width');
}
updateTables();
};
EDIT: Corrected the onresize code.
The problem as given can actually be solved rather straightforward with nesting CSS grid layouts.
(If there there was colspan or rowspan that would require using their counterparts grid-column and grid-row.)
I simplified the HTML so the solution can be understood more clearly - though that loses the semantics of the table.
If it's desired to maintain the table markup then the intervening tags (e.g. the <tr>) need to be styled as display:contents to ensure they don't break up the layout (like here).
/* defining the grid */
.grid{display:grid;grid-gap:0 0;}
.grid-2col{grid-template-columns:auto auto;}
.grid-3col{grid-template-columns:auto auto auto;}
/* making it look like a table. */
.th{font-weight:bold;}
.grid>*{
border:1px solid black;
padding:5px;}
/* avoiding double borders. */
/* no double top border - first row can be recognized by class .th */
.grid>*{border-top:none;}
.th{border-top:1px solid black;}
/* no double side border - first column can be recognized modulo number of columns of grid. */
.grid>*{border-left:none;}
.grid-2col>*:nth-child(2n+1){border-left:1px solid black;}
.grid-3col>*:nth-child(3n+1){border-left:1px solid black;}
/* no padding in nested tables. */
.grid>.grid{border:none;padding:0px;}
.grid>.grid>*{border-left:none;border-top:none;}
<div class="grid grid-3col">
<span class="th">Customer</span>
<span class="th">Items</span>
<span class="th">Payments</span>
<span>Customer Name</span>
<div class="grid grid-3col">
<span>Item1</span>
<span>5</span>
<span>$400.00</span>
<span>Item2</span>
<span>10</span>
<span>$200.00</span>
<span>Item3</span>
<span>2</span>
<span>$500.00</span>
</div>
<div class="grid grid-2col">
<span>12 Sep 2018</span>
<span>$3,000.00</span>
<span>18 Sep 2018</span>
<span>$2,000.00</span>
</div>
</div>
The meaning of the <table> tag has changed between current HTML and older HTML. While <table> used to server more of a formatting function, in HTML5 it should only be used for a literal table. As others have mentioned, your setup doesn't really look like a table in the literal sense. So while using a bunch of nested <table> elements could accomplish what you want, it's probably not the most effective way.
You could accomplish this by just nesting a bunch of <div> elements along with some basic CSS. Here's an example:
<div style="width: 1000px; height: 600px; border: 1px solid #000;">
<div style="width: 100px; height: 600px; display: inline-block; border: 1px solid #000; margin: 0; padding: 0;">X</div>
<div style="width: 100px; height: 300px; display: inline-block; border: 1px solid #000; margin: 0; padding: 0;">Y</div>
<div style="width: 100px; height: 300px; display: inline-block; border: 1px solid #000; position: relative; top: 300px; margin: 0; padding: 0; left: -105px;">Z</div>
</div>
This will produce the X, Y, and Z parts of the display you describe above. The same technique can be expanded to build the rest of your display.
A few things to note:
This example uses PXs to determine width, which is really bad practice form a CSS perspective. It would be too big on mobile and too small on a large desktop. You could, however, adapt this technique to use a more adaptive format. This will add complexity to the solution though, hence why I chose to use PXs for the example.
This example uses inline CSS since it's the easiest way to show the solution. However, this is really bad practice, DON'T ACTUALLY DO THIS!
Hope this helps.
Online spreadsheet programs like Google Sheets make it look so easy, but when I first dove in, it was much more complicated. I gave each row a special attribute like row="r_1" and each subrow specific attributes like row="r_1" subrow="sr_1_a". onkeyup events or an onload triggered math to find all parent rows using querySelectorAll, then made sure their height was equal to all the subrow heights added up.
In my opinion I assume that it'd be hard to achieve what you want.
Reasons: td are nested in tr, so any change in height will affect height of of the associated row.
Suggestion: you can have five separate tabs or divs and have them displayed inline-block, then put your data in them.
If you use this suggestion, please first set a div that wraps the 5 columns and give it a font-size of 0px, then remember to change the font-size to something useful for the children.
I have two divs side by side within a parent div - the left div will contain text, while the right div will contain an image, and on button click, the right div can expand, or reduce back to its original width.
<style>
.parent{
width: 100%;
border: 1px solid blue;
padding: 2px;
float: left;
}
.left{
width: 60%;
float: left;
border: 1px solid red;
}
.right{
width: 38%;
border: 1px solid orange;
float: right;
}
</style>
<input type="submit" class="toggle_div" id="button1" value="Expand"/><br>
<div class="parent">
<div class="left">Left Content</div>
<div class="right">Right Content</div>
</div>
javascript to expand/reduce the div:
$("#button1").click(function(){
var inputValue=$("#button1").attr('value');
if(inputValue=="Expand")
{
$(".right").animate({width:"60%"});
$("#button1").attr('value','Reduce');
}
else if(inputValue=="Reduce")
{
$(".right").animate({width:"38%"});
$("#button1").attr('value','Expand');
}
});
Right now, when I increase the width of the right div, it slides underneath the left div. But what I want is for the left div to reduce in size accordingly, and take on the remaining width available within the parent, with left and right div remaining side by side.
JSFiddle
My css is weak, and I'm guessing this is something I can do in css, without having to use javascript to resize the left div too. Suggestions appreciated as always.
Your left div just wasn't being updated with the correct size so your container was more than 100%. I've fixed it here:
$("#button1").click(function(){
var inputValue=$("#button1").attr('value');
if(inputValue=="Expand")
{
$(".right").animate({width:"60%"});
$(".left").animate({width:"38%"});
$("#button1").attr('value','Reduce');
}
else if(inputValue=="Reduce")
{
$(".right").animate({width:"38%"});
$(".left").animate({width:"60%"});
$("#button1").attr('value','Expand');
}
});
.parent{
width: 100%;
border: 1px solid blue;
padding: 2px;
float: left;
}
.left{
width: 60%;
float: left;
border: 1px solid red;
}
.right{
width: 38%;
border: 1px solid orange;
float: right;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="submit" class="toggle_div" id="button1" value="Expand"/><br>
<div class="parent">
<div class="left">Left Content</div>
<div class="right">Right Content</div>
</div>
not really good myself, but maybe something like http://jsfiddle.net/fd9pos8m/ ? not smooth though
<div style="padding:2px; width:500px; background: #FFFFFF ">
<div class="left" style="float:left; width:50%; background: #ff0000 ">Left Content</div>
<div class="right" style="margin-left:52%; width:38%; background: #000000 ">Right</div>
<div style="clear:both"></div>
<input type="button" id="button1" value="expand">
</div>
<script>
$('#button1').click(function(){
var val = $(this).val();
if(val == 'expand') {
$('.right').animate({width:'60%'});
$(this).val('reduce');
} else {
$('.right').animate({width:'38%'});
$(this).val('expand');
}
});
</script>
Here try this does this work? http://jsfiddle.net/CFNUJ/917/ the #right-bg { is staying to the #left-bg
the first is using just css
How about using css flexbox? You don't have to worry about that .left or .right jumps down when there's no enough space for them, it just handles that situation for you.
Do remember to check the browser support for flexbox though.
I only tweaked the css:
.parent{
width: 100%;
border: 1px solid blue;
padding: 2px;
display: flex;
}
.left{
width: 60%;
border: 1px solid red;
}
.right{
width: 38%;
border: 1px solid orange;
}
jsfiddle
http://jsfiddle.net/sen3t6vk/74/
I want to show a popup using qTip1 with 2 divs, the first one contains a picture and has to be on the left, the second one has some text in a table and has to be on the right. The problem is when I create the inner HTML for qTip, the table with the text is always under the picture and not on the right. I've tried some solutions here on stackoverflow but I think I'm missing something.
This is what the generated inner HTML for qTip looks like:
<div id="infoBoxParent" style="margin: 20px 20px 0 0; border: 1px solid #333; overflow: auto">
<div id="infoBoxPicture" style="float: left; border: 1px solid green;"><img
src="foo.png"
alt="" width="111" height="170" class="photo left" style="float: left;"/></div>
<div id="infoBoxFacts" style="float: right; border: 1px solid red; overflow: hidden">
<table>
<tr>
<td style='padding:5px;text-align:left;'>Some Text:</td>
<td style='padding:5px;text-align:right;'>Stack Overflow is a question and answer site for professional
and enthusiast programmers. It's built and run by you as part of the Stack Exchange network of Q&A
sites. With your help, we're working together to build a library of detailed answers to every
question about programming.
</td>
</tr>
</table>
</div>
</div>
What am I doing wrong?
If I am understanding your question this should work. It is from a css framework I built(Responder). I removed a lot of code so it highlights the solution to your question. The .reponsive-image class is not necessary but I added it because you are using images in your project.
If you want to change the width of your columns you can add classes to your style sheet in the folowing fashion:
.column25{
max-width:25%;
width:25%:
}
There is a link below for Responder that has a lot of these classes typed out already if you need to copy them.
Link to Solution Preview: http://codepen.io/larryjoelane/pen/OMEoMq
Link to Responder CSS framework: http://codepen.io/larryjoelane/pen/XmzQba
CSS:
/*makes an image responsive*/
.responsive-image {
display: block;
height: auto;
max-width: 100%;
width: 100%;
}
/* responsive container for the column classes*/
.row {
/*set the max width of the .row class to
*to 100% so the columns within it do not exceed
*a sum of 100% combined
*/
max-width: 100%;
/*keeps the .row divs next each other when the screen
resizes*/
overflow: hidden;
}
.row div {
/* adjust the aspect of the font
* so it displays well and within the div elements
* when the screen is resized
*
*/
font-size-adjust: 0.45;
line-height: 1.5;
/*provide some spacing in between the lines*/
float: left;
/*removes spacing between in line elements*/
clear: none;
/*removes spacing between in line elements*/
display: inline-block;
/*make the div elements align horizonatally*/
/*styling below prevents padding and borders from breaking
the max-width setting of the columns*/
-webkit-box-sizing: border-box;
/* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box;
/* Firefox, other Gecko */
box-sizing: border-box;
/* Opera/IE 8+ */
/*allow long words to wrap to another line*/
word-wrap: break-word;
}
/*begin section for styling the column widths*/
.column50 {
max-width: 50%;
width: 50%;
}
HTML:
<div class="row" id="infoBoxParent" style="border: 1px solid #333; overflow: auto">
<div class="column50" id="infoBoxPicture" style="border: 1px solid green;"><img src="foo.png" alt="foo image" width="111" height="170" class="photo left" style="" /></div>
<div class="column50" id="infoBoxFacts" style="border: 1px solid red; overflow: hidden">
<table>
<tr>
<td style='padding:5px;text-align:left;'>Some Text:</td>
<td style='padding:5px;text-align:right;'>Stack Overflow is a question and answer site for professional and enthusiast programmers. It's built and run by you as part of the Stack Exchange network of Q&A sites. With your help, we're working together to build a library of detailed answers
to every question about programming.
</td>
</tr>
</table>
</div>
</div>
You could try to remove all the inline css and put the following code in the header.
This should work.
<style>
#infoBoxParent {
margin: 20px 20px 0 0;
border: 1px solid #333;
overflow: auto;
width: 100%;
}
#infoBoxParent div {
position: relative;
float: left;
border: 1px solid;
}
#infoBoxPicture{
border-color: green;
width: 30%;
}
#infoBoxFacts{
border-color: red;
width: 68%; /* 2% margin for the lines */
}
</style>
i am using three div tags like this
<div id="main">
<div id="content">
<div id='sub'>
Child div
</div>
</div>
</div>
css
#main
{
width:auto;
height:auto;
margin-left:-100px;
}
#content{
width:auto;
height:auto;
border:5px solid blue;
}
#sub
{
width:100%;
height:100%;
border:1px solid red;
}
so i kept everything auto. But even when i resize or change the margin of main parent the last child not adapting to it.
I am not sure what may be the problem. But when i set width and height as 100% in resize or margin change event it seems adapting.
Can anyone help me out in this? And also is there anyway to detect the parent div attribute changes in some event??
JSFIDDLE
Try This
$(document).ready(function() {
$('#sub').css({
width: $('#content').width(),
height: $('#content').height()
});
$('button').click(function() {
$('#main').css('margin-left', '0px');
$('#sub').css({
width: $('#content').width(),
height: $('#content').height()
});
});
});
#main {
width: auto;
height: auto;
margin-left: -100px;
}
#content {
width: auto;
height: auto;
border: 5px solid blue;
}
#sub {
width: 100%;
height: 100%;
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<div id="main">
<div id="content">
<div id='sub'>
Child div
</div>
</div>
</div>
<button>margin0</button>
Working Demo
You are setting the width from JS. Try this.
<div id="main">
<div id="content">
<div id='sub'>
Child div
</div>
</div>
</div>
<button>margin0</button>
#main
{
margin-left:-100px;
}
#content{
border:5px solid blue;
}
#sub{
display: block;
border:1px solid red;
}
$(document).ready(function(){
$('#sub').css('height',$('#content').height());
$('button').click(function(){
$('#main').css('margin-left','0px');
});
});
JSFiddle
I read in some forums that to make an html table look disabled is to add a layer of div. My problem is I don't know how to do it.
I have 3 questions:
How will I set the div height that it will automatically adjust to the table height whenever the table increases its height when a new row is added.
How will I make the div cover the table. I don't know how to layer html elements.
How am I going to code the javascript that will make my table look disabled when I click 'Disable' button and enable it again when I click 'Enable' button.
tabledisabletest.html
<html>
<head>
<script type="text/javascript">
</script>
<style type="text/css">
table#tblTest {
width: 100%;
margin-top: 10px;
font-family: verdana,arial,sans-serif;
font-size:12px;
color:#333333;
border-width: 1px;
border-color: #666666;
border-collapse: collapse;
}
table#tblTest tr.highlight td {
background-color: #8888ff;
}
table#tblTest tr.normal {
background-color: #ffffff;
}
table#tblTest th {
white-space: nowrap;
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #dedede;
}
table#tblTest td {
border-width: 1px;
padding: 8px;
border-style: solid;
border-color: #666666;
background-color: #ffffff;
}
#disabler {
width: 100%;
height: 200px;
background-color: #bbb;
opacity:0.6;
}
</style>
</head>
<body>
<div id="disabler"></div>
<table id="tblTest">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>UK </td>
</tr>
<tr>
<td>Henrik</td>
<td>Denmark</td>
</tr>
<tr>
<td>Lionel</td>
<td>Italy</td>
</tr>
<tr>
<td>Ricardo</td>
<td>Brazil</td>
</tr>
<tr>
<td>Cristiano</td>
<td>Portugal</td>
</tr>
</tbody>
</table>
<input type="button" onclick="disable = true;" value="Disable" />
<input type="button" onclick="disable = false;" value="Enable" />
</body>
</html>
I have the div disabler to do the disabling but I can't make it cover the table.
Please help me with this. I'm so new to this thing.
If you want the disabler element to overlay your table, add a negative bottom-margin to it. Also, set opacity to a value lower than 1, to not completely cover (hide) the table behind it.
#disabler {
opacity: 0.5;
margin-bottom: -200px;
}
Since you've mentioned that you're doing this for educative purposes, I won't give the full solution. See this fiddle to get started.
If you want to make a text look "unselectable", use the following CSS:
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
You will have to place the div disabler on top of the table.
You can do so by absolutely positioning the div. I added a new div, tableContainer enveloping the disabler div and the table, and absolutely positioning the div.
<div id="tableContainer"> <!-- New Div-->
<div id="disabler"></div>
<table>....<table>
</div>
Add position: absolute; to the #disabler
And most importantly write the javascript to display and hide the div:
function disableTable()
{
document.getElementById("disabler").style.display = "block";
}
function enableTable()
{
document.getElementById("disabler").style.display = "none";
}
Live Example: http://jsbin.com/icuwug