打印样式表方法:黑名单与白名单

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

“黑名单”是打印样式表的一种常用方法。我们知道,如果用户打印我们网站上的文章,可能不需要看到我们的网站导航。因此,我们会将其隐藏起来,就像隐藏屏幕上的内容一样(display: none;)。

有没有办法反过来操作呢?

黑名单技术

您可以选择页面上所有不想打印的元素,并将它们放入一些应用于打印媒体的 CSS 中。也许可以在主样式表底部的一个块中。

@media print {
  .main-navigation, .comments, .sidebar, .footer {
     display: none;
   }
}

或者,您可以将大部分工作留给 HTML。您可以创建一个“不打印”类,并根据需要应用它。

@media print {
  .dont-print {
    display: none;
  }
}
<section id="comments" class="comments dont-print">
</section>

黑名单是一种常见的策略,在互联网上发布的打印样式表技巧文章中随处可见。

唯一的麻烦是它需要维护。HTML 会发生变化,因此您需要维护不想打印的选择器列表或 HTML 中的类,以确保它们位于正确的元素上。很容易忘记。

白名单技术

白名单将采用相反的技术。在您的打印样式中,除了您明确选择的内容外,所有内容都将从打印中隐藏。

不过,不要抱太大希望,这很难实现。我首先想到的是普遍隐藏所有内容,然后使用类覆盖。

/* Bad idea #1 */
@media print {
  * {
    display: none;
  }
  .print-me {
    display: block;
  }
}
<main class="main-content print-me">
</main>

这里有两个问题

  1. display 属性不会继承,因此即使您告诉某个元素再次显示自身,其子元素仍将被通用选择器选中并隐藏。
  2. 您告诉要显示的元素的父元素可能仍处于隐藏状态。

我们可以使用以下方法解决后者……

/* Bad idea #2 */
@media print {
  * {
    display: none;
  }
  .print-me,
  .print-me * {
    display: block;
  }
}

……选择子元素并使其再次显示。但这将使所有子元素都成为块级元素,这很糟糕。您不希望您的<a><em><strong>以及其他任何inlineinline-blockinline-tableinline-flex等元素变成块级元素。

这将比黑名单更难维护。

我考虑过手动要求所有父元素也具有“print-me”类,但这会使同级元素也打印出来,而它们不应该打印(没有该类)。我尝试使用以下方法修复它:

/* Bad idea #3 (addition to previous) */
.print-me ~ *:not(.print-me) {
  display: none;
}

但这无法处理之前的同级元素。

这可能是 CSS 中父选择器的一个非常好的用例,因为您可以潜在地使用父选择器来取消隐藏父元素,如果它包含具有特定类名的元素。理论上类似于*:contains(.print-me) { display: block; }

可能还存在纯 CSS 的解决方案。我承认没有在这上面花费数小时。我可能错过了某种巧妙的策略。我知道visibility 属性不会继承,因此可能存在潜在的可能性,但这不会像您希望的那样影响布局。我不确定:not() 是否已充分发挥其潜力。

如果您想尝试一些想法,这里有一个测试页面供您使用。它只有一个按钮,可以切换一堆内容的父元素上的类,因此您可以假装该类类似于打印样式,并使其执行您想要的操作。

我迄今为止最接近的尝试是**放弃并使用 JavaScript** 的尝试。此处使用 jQuery 来方便 DOM 遍历,其核心是自动将类应用于元素的所有父元素,确保它们可以打印

$(".print-me")
  .parents()
  .addClass("js-print-me");

请注意,我使用的是略微不同的类名,因此它不受选择所有后代以显示的相同规则的影响。

@media print {
  * {
    display: none;
  }
  .print-me,
  .print-me * {
    display: block;
  }
  .js-print-me {
    display: block;
  }
}

甚至有一些类似于检测打印事件的方法,因此当用户要打印时,您可以应用/删除父类名以使页面处于此状态。

但这并不能解决 inline-* 问题(它们变成块级元素而不是保留其使用的显示值),这使得它相当不可用。如果您认为 JavaScript 在此处是可以接受的选择,您也可以将隐藏工作转移到 JavaScript 中,仅在*保存其原始显示类型后*应用display: none;,以便在完成后可以将其放回。我认为 jQuery 甚至已经以某种方式做到了这一点?

有传言称未来某些 CSS 东西可以以一种不将其绑定到布局的方式处理可见性的切换(并且这是一个非常好的用例)。