如何在 HTML email 中隐藏没有数据的空行?
结论
不要试图在收件人的 email client 中隐藏空行。发送邮件前,应根据 T-SQL 返回的数据动态生成 <tr>。查询返回九条 invoice,就生成九行 HTML;没有数据的行不应出现在邮件正文中。
HTML email 不适合使用 JavaScript、jQuery 或其他依赖客户端脚本执行的前端模板。可以用 ASP.NET Repeater、Razor 模板或普通 C# 字符串渲染来生成表格行。
为什么不能依靠 jQuery
大多数 email clients 不会执行邮件中的 JavaScript,外部脚本通常也无法加载。所以下面的代码即使在浏览器中有效,放进邮件后也不可靠:
$(function () {
$('.row').each(function () {
if ($(this).val().trim() === '') {
$(this).hide();
}
});
});
另外,.val() 主要用于表单控件,不适合读取 <tr> 或普通 <td> 中的文本。即使改用 .text(),也无法解决 email client 禁止脚本的问题。
border: none; 只能让空单元格不那么显眼,并不会删除空行。如果行上有 height、padding 或换行,页面中仍可能出现空白。
推荐的处理流程
生成邮件时,可以按以下顺序处理:
- 让 T-SQL 只返回有效的 invoice 记录。
- 在 .NET 中将查询结果映射为 invoice 列表。
- 让模板遍历列表,每条记录生成一个
<tr>。 - 模板渲染完成后,发送最终的 HTML。
例如,可以在查询中排除没有实际内容的记录:
SELECT
InvoiceNumber,
Description,
Amount
FROM dbo.Invoice
WHERE PaymentId = @PaymentId
AND InvoiceNumber IS NOT NULL
ORDER BY InvoiceNumber;
如果需要根据多个字段判断一条记录是否有效,应在查询或业务层中写清楚判断条件,不要因为某个字段为空就误删有效记录。
使用 ASP.NET Repeater
如果项目使用 ASP.NET Web Forms,可以用 Repeater 生成数量不固定的表格行:
<table role="presentation"
width="100%"
cellpadding="0"
cellspacing="0"
style="border-collapse:collapse;">
<tr>
<th align="left" style="padding:8px;border-bottom:1px solid #cccccc;">
Invoice
</th>
<th align="left" style="padding:8px;border-bottom:1px solid #cccccc;">
Description
</th>
<th align="right" style="padding:8px;border-bottom:1px solid #cccccc;">
Amount
</th>
</tr>
<asp:Repeater ID="InvoiceRepeater" runat="server">
<ItemTemplate>
<tr>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
<%#: Eval("InvoiceNumber") %>
</td>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
<%#: Eval("Description") %>
</td>
<td align="right" style="padding:8px;border-bottom:1px solid #eeeeee;">
<%#: Eval("Amount", "{0:C}") %>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
发送邮件前绑定数据并渲染模板:
InvoiceRepeater.DataSource = invoices;
InvoiceRepeater.DataBind();
这里要保证 invoices 只包含有效记录。Repeater 不会自动补足 30 行,列表中有多少条数据,它就生成多少个 <tr>。
使用 Razor 模板
如果项目使用 ASP.NET Core、MVC 或其他支持 Razor 的邮件模板方案,可以直接遍历集合:
<p>Check Date: @Model.CheckDate.ToString("MM/dd/yyyy")</p>
<table role="presentation"
width="100%"
cellpadding="0"
cellspacing="0"
style="border-collapse:collapse;">
<thead>
<tr>
<th align="left" style="padding:8px;border-bottom:1px solid #cccccc;">
Invoice
</th>
<th align="left" style="padding:8px;border-bottom:1px solid #cccccc;">
Description
</th>
<th align="right" style="padding:8px;border-bottom:1px solid #cccccc;">
Amount
</th>
</tr>
</thead>
<tbody>
@foreach (var invoice in Model.Invoices)
{
<tr>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
@invoice.InvoiceNumber
</td>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
@invoice.Description
</td>
<td align="right" style="padding:8px;border-bottom:1px solid #eeeeee;">
@invoice.Amount.ToString("C")
</td>
</tr>
}
</tbody>
</table>
当 Model.Invoices 中只有九项时,最终邮件也只会有九个数据行。
如果完全没有明细,也可以单独处理:
@if (Model.Invoices.Count > 0)
{
// 输出明细表格
}
else
{
<p>本次付款没有可显示的 invoice 明细。</p>
}
使用现有占位符模板
如果当前邮件系统只支持 @@invoicenum1@@ 这类固定占位符,最好让模板引擎支持循环。例如:
{{#each invoices}}
<tr>
<td>{{invoiceNumber}}</td>
<td>{{description}}</td>
<td>{{amount}}</td>
</tr>
{{/each}}
每种模板引擎的循环语法不同,应以项目实际使用的模板系统为准。如果模板引擎完全不支持循环或条件块,可以采用以下方法:
- 在 .NET 中生成完整的明细行 HTML,再替换一个整体占位符,例如
@@invoiceRows@@。 - 继续保留 30 个固定行,但在服务端删除没有数据的整段
<tr>。
不要只向固定的 <td> 中写入空字符串,因为对应的 <tr> 依然存在。
在 C# 中直接生成行
如果没有现成的模板引擎,也可以在发送邮件前直接生成明细行:
using System.Net;
using System.Text;
var rows = new StringBuilder();
foreach (var invoice in invoices)
{
rows.AppendLine($"""
<tr>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
{WebUtility.HtmlEncode(invoice.InvoiceNumber)}
</td>
<td style="padding:8px;border-bottom:1px solid #eeeeee;">
{WebUtility.HtmlEncode(invoice.Description)}
</td>
<td align="right" style="padding:8px;border-bottom:1px solid #eeeeee;">
{invoice.Amount:C}
</td>
</tr>
""");
}
string emailHtml = template.Replace("@@invoiceRows@@", rows.ToString());
必须对 InvoiceNumber 和 Description 做 HTML 编码,防止 &、<、> 等字符破坏邮件结构,也避免数据库中的内容被当作 HTML 执行。金额应按数值格式化,不要直接拼接未经验证的文本。
HTML email 的注意事项
编写邮件表格时,应采用兼容性较好的方式:
- 将重要样式写成 inline styles。
- 使用
<table>、<tr>和<td>完成主要布局。 - 不要依赖 JavaScript、jQuery、复杂 CSS 选择器或客户端 DOM 操作。
- 同时生成纯文本版本,方便无法显示 HTML 的客户端阅读。
- 在需要支持的 Outlook、Gmail、Apple Mail 等客户端中实际测试。
- 日期在表格上方显示一次即可,也可以作为公共字段传入模板,不必在每个 invoice 行中重复。
是否应该限制显示数量
将展示数量限制为 5、10 或 30 项,属于产品规则,不是 HTML email 的技术限制。明细较少时可以全部展示;明细很多时,可以只显示前若干项,并附上安全的详情页链接。
例如:
const int maxRowsInEmail = 10;
var visibleInvoices = invoices.Take(maxRowsInEmail).ToList();
var remainingCount = invoices.Count - visibleInvoices.Count;
当 remainingCount > 0 时,可以在表格后显示:
<p>
另有 12 条明细未在邮件中展示。
<a href="https://example.com/payments/123">查看完整付款明细</a>
</p>
详情链接应使用 HTTPS,并要求用户正常登录。不要只凭可猜测的付款编号开放敏感财务数据。无论是否设置展示上限,处理方式都一样:服务端根据实际数据生成邮件,不要先发送 30 个空行,再让 email client 隐藏它们。
备注:内容仅供参考。
