PAYATHON 2026

如何在 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 或换行,页面中仍可能出现空白。

推荐的处理流程

生成邮件时,可以按以下顺序处理:

  1. 让 T-SQL 只返回有效的 invoice 记录。
  2. 在 .NET 中将查询结果映射为 invoice 列表。
  3. 让模板遍历列表,每条记录生成一个 <tr>。
  4. 模板渲染完成后,发送最终的 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 隐藏它们。

备注:内容仅供参考。