都 9102 年了 HTML 邮件总不会还得用 <table> 吧?

答案是没错你还得用…… 最近又跳进 HTML 邮件大坑,实现了一版响应式的 HTML 邮件,这里记录一下,提醒自己下次不要再接这样的需求不能再犯同样的错误。

客户端支持情况

webmail 对 HTML 邮件的支持程度普遍不错,允许使用 <style> 、绝大多数布局相关的 CSS 属性(position、float、甚至 flex)都支持,比较尴尬的是基本上每一家都会有一些不太准确的“优化”,Gmail、QQ 邮箱都有。

客户端是水最深的地方,桌面版的 Mail.app、Outlook 对 HTML 邮件的支持程度非常好,没怎么出问题,但是移动端就非常狂野了,表现最差的是 QQ 邮箱。但这些都比不过 Windows 版 Outlook,稳坐业界毒瘤一把手。

Gmail

2016 年开始支持内联 <style> 和 media query,因此可以放心实现响应式布局。需要注意的有:

  • <style> 必须在 <head> 里面,而且貌似 <head> 里不能包含其他内容(比如 <meta> );
  • <style> 内容不能超过 8K,不能嵌套 @ (比如在 @media 区块里写 @font-face );
  • 会为了覆盖文字颜色修改邮件 DOM 结构,给内容节点包额外的节点;
  • 不支持引入 SVG,只能用 JPG、PNG、GIF 等常规图片格式;
  • 除了 <td background=""> ,不支持图片背景 background-image
  • 可以针对 Gmail 写样式(比较 hack,庆幸暂时没用上);

其他参考 https://www.emailonacid.com/blog/article/email-development/12_things_you_must_know_when_developing_for_gmail_and_gmail_mobile_apps-2/

网易邮箱

web 端没什么问题,移动端所推的网易邮箱大师对布局支持并不好,需要准确复刻设计稿的话建议回归 <table> +固定宽度,放弃响应式。

网易邮箱大师有个独特的优化,如果某个节点的字数超出宽度限制而且有设置字号,不管这个节点有没有 text-overflow: ellipsis; overflow: hidden; 之类,它都会尝试缩减字号以确保当前宽度能够显示全部文字。不设置字号反而不会有这个问题。

如果这个节点除了自身宽度还有边距等可能占据水平空间的属性,整个页面的布局都会被影响。

QQ 邮箱

web 端支持内联 <style> 并且会统一给选择器加上 .qmbox 前缀, @media 区块里的第一个选择器会漏加,可能是用来加前缀的正则表达式考虑不充分……

移动端不支持内联 <style> 而且字号选择有点诡异。

阿里邮箱

应该说网页端支持程度不错,但是有几个非常坑的:

  • 支持 <style> 但是会删掉所有的 !important ,极大限制了邮件模板的兼容策略(最坑的没有之一);
  • 会自动处理 <table> 增加一层 <div> 包裹而且很容易算错宽度(反过来说就是内容得写死宽度);

移动端也有一些需要注意:

  • 不支持图片背景 background-image ,但允许 <td background="">
  • <style> 中如果有错误的样式,会导致之后的样式全都不生效;

Outlook

Office 365 Mac 版 Outlook 用的是 webkit,兼容性非常好,无论是网页版还是客户端,都支持内联 <style> ,计算动态宽度也没有问题。然而 Windows 版 Outlook 用来渲染邮件正文的还是 Word 2007 HTML,兼容性非常差,即便是 Office 365 版本也是如此。需要支持的话只能用 <table> ,详细可以参考 https://segmentfault.com/a/1190000008864116

兼容过 IE6-8 的前端开发应该很熟悉下面这个针对 Word 2007 HTML 的 hack:

  1. <!--[if mso]><![endif]-->

更多针对 mso 的 hack 可以参考 https://cm.engineering/fixing-bugs-with-outlook-specific-css-f4b8ae5be4f4

实际编写过程中发现的问题:

  • div 宽度设置不管用,水平居中一定要用表格;
  • div 嵌套之后,外部 div 背景色填充有问题,只有内部 div 有内容的地方会显示背景色,整体效果支离破碎;

<table> 吧……

噢,还有一个特别管用的调试建议,由于 Windows Outlook 和 Word 用的是同一个渲染引擎(Word 2007 HTML),你可以通过在 Word 查看 html 来调试样式。

Takeaways

响应式布局

先上一个通用的响应式 HTML 邮件模板:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <style>
  5. .root { max-width: 375px; }
  6. @media (min-width: 600px) {
  7. .root { max-width: 600px; }
  8. }
  9. @media (min-width: 750px) {
  10. .root { max-width: 750px; }
  11. }
  12. </style>
  13. </head>
  14. <!--[if mso]><body style="margin: 0; text-align: center;"><table width="600" cellspacing="0" cellpadding="0" style="margin: 0 auto;"><tr><td><![endif]-->
  15. <!--[if !mso]><!--><body style="margin: 0;"><!--<![endif]-->
  16. <div class="root" style="margin: 0 auto; text-align: left"></div>
  17. <!--[if mso]></td></tr></table><![endif]-->
  18. </body>
  19. </html>
  • Windows Outlook 使用 table 实现水平居中并定宽 600px
  • 其他客户端使用 .root 节点实现响应式(375px、600px、750px 三档)以及水平居中

Spark 收件箱查看的时候会无视 .root { text-align: left; max-width: 600px } 同时其他邮箱客户端在没有 body { text-align: center; } 的情况下都可以水平居中 .root

响应式字号

不考虑阿里邮箱的话可以直接用 font-size: 42px !important; ,要考虑阿里邮箱的话需要如下 hack:

  1. <div class="root">
  2. <h1 style="font-size: 20px"><span></span></h1>
  3. </div>
  4. <style>
  5. @media (min-width: 750px) {
  6. /* 阿里邮箱会过滤样式中的 !important */
  7. h1 span { font-size: 24px; }
  8. }
  9. </style>