grouping tr but browser closes tbody prematurely - dom

When using LitElement to render data dynamically, the browser inserts tbody all the time negating any effort to "group" table rows.
render() {
return html`
<table style="border-collapse:collapse;border:solid 1px #666;">
${this.rows.map((row)=>{
var disp= html`
${(row=="FOUR")?html`</tbody>`:''}
${(row=="TWO")?html`
<tbody style="border:solid 2px #F00; border-collapse:separate;">
<tr>
<td>SOME HEADER</td>
</tr>
`:''}
<tr>
<td>${row}</td>
</tr>
`
return disp;
})}
</table>
`;
} //render()
constructor() {
super();
this.rows = ['ONE','TWO','THREE','FOUR'];
}
The result is the tbody is closed immediately after the closing tr of "SOME HEADER" instead of the tbody being closed after the tr "FOUR" which is what we want in this case.
It looks like the browser does this by itself because it always wants to insert a tbody whenever a tr is written to the DOM?
So I suspect this would happen to any framework that renders data dynamically - but have not verified this.
I assume this is not going to be "fixed" anytime soon.
That being the case, anyone have a suggestion on how to group tr's together in a block in this case?
Thanks!

If you have an unclosed <tbody> in a document fragment the browser will close it for you.
Instead nest the <tr> you want to group inside a template that holds both the <tbody> and closing </tbody>:
const groups = //process this.rows into groups
return html`
<table style="border-collapse:collapse;border:solid 1px #666;">
${groups.map(g => html`
<tbody style="border:solid 2px #F00; border-collapse:separate;">
<tr><th>${g.name}</th></tr>
${g.rows.map(r => html`
<tr><td>${r}</td></tr>`)}
</tbody>`)}
</table>`;

Related

Html email and fluid table with changing borders

I have some design requirements for an email template where I have two "challenges":
two columns need to flip to one column
some visible border lines need to be switched from vertical to horizontal
The following shows how it should look (2 columns on the left for desktop, 1 column on the right for mobile):
The whole email is based on responsive tables and the two-column part is implemented as follows right now:
<table cellpadding="5" cellspacing="0"
style="background-color:#F6F6F6; font-size: 14px; color:#58595b; width:100%; border-collapse:collapse;">
<tr><td align="center" valign="top" height="10" colspan=2 style="line-height: 10px; font-size: 10px;"> </td></tr>
<tr>
<td valign="top" style="border-right: 1.5px solid; border-color: #d0d0d0; padding-right:40px; text-align:right; width:42%; vertical-align:top;">
Start point
</td>
<td valign="top" style="padding-left:40px; vertical-align:top;">
<strong>Fri, January 12, 2023 12:00</strong>
<br />
Harbour, Seatown
</td>
</tr>
<tr><td align="center" valign="top" height="10" colspan=2 style="line-height: 10px; font-size: 10px;"> </td></tr>
<tr>
<td valign="top" style="border-right: 1.5px solid; border-color: #d0d0d0; padding-right:40px; text-align:right; width:42%; vertical-align:top;">
End point
</td>
<td valign="top" style="padding-left:40px; vertical-align:top;">
<strong>So, January 18, 2023 10:00</strong>
<br />
Central Station, Capital
</td>
</tr>
<tr><td align="center" valign="top" height="10" colspan=2 style="line-height: 10px; font-size: 10px;"> </td></tr>
</table>
I tried the approach with having a left and a right table (explained here) but the problem is that I do not use fixed widths.
How could I achieve the required design with a responsive mail template?
You will need to use the technique as outlined in the link, but if you want to use percentages instead of fixed widths, then use width="50%".
This is because the technique works on the basic fundamentals of HTML, that if a block doesn't fit in the space available, it will automatically shift underneath.
So to enable the stacking without a fixed pixel width, you will need to add a #media query to force the stacking (otherwise it would not stack).
e.g.
#media (max-width: 620px)
.table-left,.table-right {
width: 100% !important;
}
(The article you link to is a bit outdated: don't use [class=...], just write it out normally. Gmail may strip the entire <style> section if it doesn't like something in it, and this is one of those things it doesn't like.)
I prefer an override (max-width, and !important) because you want everything possible inline, and only to use embedded styles where strictly necessary.
But that's also why it's best to use a fixed pixel width, because some email clients do not respect your embedded styles (styles in the <head>). GANGA emails (one form of Gmail account) fall into this category. Those email clients would not stack even though they may need to, if you fully rely on the #media query for the stacking.
To override the border, put a style on the <td>, and reference it in the #media query, e.g.
#media (max-width: 620px)
.border {
border-right:none!important;
}
.border-top {
border-bottom:1.5px solid #d0d0d0 !important;
}
As one doesn't have the same border structure (they don't both need border-bottom), one of the <td>s will need a different class. Here, I'm expecting the first one, i.e. <td class="border border-top">.

Freemarker Loop to write tables

This is for email coding.
Seems simple in my head, but I have assigned a set of data to a freemarker variable
[#assign LOOP_TAB]${list.creative!'000000'}[/#assign]
Where list.creative contains either 1-5
I then want to write the correspsonding amount of simple HTML tables to my template
<!--Begin TEXT BOX-->
[#list 0..LOOP_TAB?length-1 as i]
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td align="left" valign="top" style="font-size: 14px; font-family: Arial, Helvetica, sans-serif; line-height: 20px; color: #000000;"><br>Loop Table Structure.<br><br></td>
</tr>
</tbody>
</table>[/#list]
Its sort of working, but im getting two tables by default, and never teh right amount of tables when i test on 2, or 4, or 1
Let me rename LOOP_TAB to numberOfTabs. So, if that's a number, and not a string, then:
[#list 1 ..< numberOfTabs!0 as _]
<table width="100%" border="0" cellspacing="0" cellpadding="0">
...
</table>
[/#list]
If numberOfTabs is a string, and you can't fix the data-model, then use (numberOfTabs?number)!0 instead of numberOfTabs!0.
Some further notes:
[#assign LOOP_TAB]${list.creative!'000000'}[/#assign] can be simply written as [#assign LOOP_TAB = list.creative!'000000']. But you don't need this at all, as you have seen.
?length gives the length of the strings in characters, so, it has returned the length of the string in LOOP_TABS.
from .. to gives an inclusive range, furthermore 0 .. -1 will give [0, -1], instead of the empty sequence that you wanted. Therefore you need from ..< to, which has exclusive end.
I used _ as loop variable only to signify that you don't actually read it anywhere. It's not special otherwise.

HTML dom insertRow()

I run into a problem while tring to add new row to Table.
The problem is the new row is added into blcok rather than block.
function myFunction() {
var table = document.getElementById("myTable");
var row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "NEW CELL1";
cell2.innerHTML = "NEW CELL2";
}
table{
border: 1px solid #ccc;
}
<table id="myTable">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
</tr>
</thead>
<tbody>
//new row is supposed to add into here.
<tbody>
</table>
<br>
<button onclick="myFunction()">Try it</button>
Thanks for any help.
You wanted to add new row to tbody, so select tbody first using getElementsByTagName("tbody") on . Also use insertRow(0) instead of insertRow(1)
function myFunction() {
var table = document.getElementById("myTable");
var tbody = table.getElementsByTagName('tbody');
console.log(tbody)
var row = tbody[0].insertRow(0);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "NEW CELL1";
cell2.innerHTML = "NEW CELL2";
}
table{
border: 1px solid #ccc;
}
<table id="myTable">
<thead>
<tr>
<th>Product</th>
<th>Description</th>
</tr>
</thead>
<tbody>
//new row is supposed to add into here.
<tbody>
</table>
<br>
<button onclick="myFunction()">Try it</button>

how can I add cellspacing to pdftable when parsing html using XMLWorker and itext

I am using XMLWorker and itext to convert html to pdf .
my html have a table and I need to set it's cellspacing =0 cellpadding=0 .
does anyone know how to do it ?
in html I saw I can replace it by setting the style :
border-collapse: collapse;
border-spacing: 0px ;
border : 0;
padding : 0;
thanks
Tami
I've tried what you're doing using the CSS you propose and it works for me:
You can find my test here: ParseHtmlTable5
This is my HTML (including the CSS): table3_css.html
<html>
<head>
<style>
table, td {
border: 1px solid green;
border-spacing: 0px;
padding: 0px;
}
</style>
</head>
<body>
<table class='test'>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Savings</th>
</tr>
<tr>
<td>Peter</td>
<td>Griffin</td>
<td>$100</td>
</tr>
<tr>
<td>Lois</td>
<td>Griffin</td>
<td>$150</td>
</tr>
<tr>
<td>Joe</td>
<td>Swanson</td>
<td>$300</td>
</tr>
<tr>
<td>Cleveland</td>
<td>Brown</td>
<td>$250</td>
</tr>
</table>
</body>
</html>
I suggest that you compare your HTML with mine to find out what you're doing wrong.
You should also use the latest version of XML Worker and iText(Sharp) as we've improved HTML parsing significantly in the latest releases.
Note that I've defined a solid, green border of 1px to prove that there is no padding and no spacing between the cells. If you change the CSS like this:
<style>
table, td {
border: 0px;
border-spacing: 0px;
padding: 0px;
}
</style>
You'll get the (ugly) version of a table without borders, without spacing between the cells and without padding inside the cells.

iPhone Safari :last-child not rendering in table

I have a responsive layout that, below a certain breakpoint, displays only the first and last columns of a table such to reduce the amount of space needed.
Here is the CSS...
#media only screen and (max-width: 749px) {
#content-container table thead th {
display: none;
}
#content-container table thead th:first-child {
display: table-cell !important;
}
#content-container table thead th:last-child {
display: table-cell !important;
}
}
#media only screen and (max-width: 749px) {
#content-container table tbody tr td {
display: none;
}
#content-container table tbody tr td:first-child {
display: table-cell !important;
}
#content-container table tbody tr td:last-child {
display: table-cell !important;
}
}
The HTML is just a simple table made of table, thead, th, tbody, td and also a attributes in the final column.
<table>
<thead>
<tr>
<th>Flight</th>
<th>Text</th>
<th>Text</th>
<th>Text</th>
<th>Text</th>
<th style="width: 140px;">Options</th>
</tr>
</thead>
<tbody>
<tr>
<td>Text</td>
<td>Text</td>
<td>Text</td>
<td>Text</td>
<td>Text</td>
<td style="width: 140px;">
<a style="display: inline-block;" href="#">Remove</a>
<a style="display: inline-block;" href="#">Brief</a>
</td>
</tr>
</tbody>
</table>
However, this produces strange results when rendering on the iPhone screen.
As you can see, there is only one column. The last column hasn't rendered.
Rotating the device landscape and then returning it to portrait makes the final column display (sometimes), which is strange. See below.
This problem is driving me up the wall and I would greatly appreciate any help!
As per the Quirksmode list, it's supported but only for static content. A possible workaround is to give the columns classes instead.