“黑名单”是打印样式表的一种常用方法。我们知道,如果用户打印我们网站上的文章,可能不需要看到我们的网站导航。因此,我们会将其隐藏起来,就像隐藏屏幕上的内容一样(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>
这里有两个问题
display
属性不会继承,因此即使您告诉某个元素再次显示自身,其子元素仍将被通用选择器选中并隐藏。- 您告诉要显示的元素的父元素可能仍处于隐藏状态。
我们可以使用以下方法解决后者……
/* Bad idea #2 */
@media print {
* {
display: none;
}
.print-me,
.print-me * {
display: block;
}
}
……选择子元素并使其再次显示。但这将使所有子元素都成为块级元素,这很糟糕。您不希望您的<a>
、<em>
、<strong>
以及其他任何inline
、inline-block
、inline-table
、inline-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 东西可以以一种不将其绑定到布局的方式处理可见性的切换(并且这是一个非常好的用例)。
我昨天在工作中刚刚尝试了这些技巧!感谢您为我澄清了一些问题。很棒的文章。
不太确定此功能的实际应用场景,而且我个人从未遇到过需要切换打印的场景。有人创建“打印优先”CSS 吗?
使用 html5 规范使用
<nav>
,通常会默认切换该块(交互式菜单在打印中已失效),并进行锚链接调整,如果可以的话,我不会再做其他操作!我几天前也尝试了这些东西,关注了你的推文。我得出了类似的结论,使用了 JavaScript,但只是循环遍历具有
print-me
类的元素并将它们附加到body
。这样嵌套就无关紧要了。查看一下:http://codepen.io/hkfoster/pen/LVmwWo如前所述,我实际上没有此功能的用例,但这仍然是一个有趣的练习。
.print-me * {display: block;}
并不是一个好主意,恕我直言。可能存在内联后代。没错!这就是我所说的
希望未来某些 CSS 可以为我们提供真正的单一职责隐藏切换。
实际上,它不必那么混乱。
您只需在打印样式表中将 html 元素的
visibility: hidden
设置为隐藏,然后创建一个具有visibility: visible
的printable
类并选择性地应用它(实际上是一种白名单技术)。之所以有效,是因为 CSS 中的
visibility
可以被子节点覆盖,而display: none
会从显示列表中丢弃整个子树。打印页面时,您将获得空白而不是隐藏的元素。
没错!在文章中
经典示例可能是侧边栏。您不希望它只是空白,而是希望它折叠成无,以便主要内容区域可以在打印页面上展开。
@Chris:在演示中,您将
.print-me
类放在浮动项目的子元素而不是浮动项目本身上的方式,它无法“跳出”并成为全宽。您可以尝试使用width: 100vw
,但这可能会变得很乱。感谢您提供此实用教程。
如何设置 display: initial;,就像之前在 CSS tricks 上讨论的那样?当应用于 .print-me,.print-me * {} 选择器时,它应该使所有子元素继承其原始状态,对吧?
Jelle
不,
display: initial;
将产生与display: block;
完全相同的结果(display 属性的初始值为 block)。有趣的是,CSS 选择器级别 4 中有一个提案,用于一个像您希望的那样工作的关键字。它目前称为
default
(尽管这可能会更改),它会将声明回滚到用户代理样式表(或用户样式表,如果存在)。由于 UA 样式表倾向于设置诸如p{display:block;}
和span{display:inline;}
之类的内容,因此您只需要.print-me,.print-me * {display:default;}
。Ugh,我讨厌在提交评论后才发现错误。我的意思是CSS *级联和继承* 4级,当然!
至少我链接是对的……
重大新闻!以前称为
default
的关键字现在是revert
。还有其他可能性可以使用
display: none
隐藏内容——我们只需要找到一个可以轻松恢复到其先前状态的方法。哦,这提出了一个有趣的问题。是的,我们需要一个更好的隐藏切换。但是也许其中一种替代的隐藏方法可以在短期内帮助我们,例如
clip()
或position-absolute-kick-off-page。您不能使用打印事件,因为它触发得很快。浏览器没有时间应用javascript类更改并正确呈现打印结果。当从浏览器界面触发打印事件时,浏览器也不允许您延迟打印事件,因为它可能被视为浏览器的错误。
即使您需要使用响应式打印样式表,某些浏览器中的打印渲染也存在错误。有时,如果您尝试更改字体系列、字体大小或对打印进行图像替换,它不起作用,因为渲染速度非常快。
打印媒体样式应该以这种方式完成
为了我们星球的利益!这就是我在我的网站上使用的方式,因为在线的内容不必在纸上。想要保留它?设置书签。就这么简单。
您可能就是那个禁用右键点击和ctrl+C的人。
不,那并不能拯救我们的星球。
这是我最好的尝试:http://codepen.io/shshaw/pen/QbxObX
首先,除了
.print-me
及其子元素外,所有内容都设置为visibility: hidden
,因此至少不需要的元素不会出现。然后,不是
.print-me
的直接后代设置为没有高度、边距或填充。如果它们包含.print-me
项,则display: table
确保它扩展以适合。width: 100%
是可选的,但有助于项目占据整个宽度。设置
font-size: 0
可确保它完全折叠,大小将使用.print-me { font-size: initial; }
重置。这里我不了解浏览器支持情况,但这似乎是一个好的开始。
这是一个好主意。非常hacky,但如果可以发展成对任何网站都像css-reset一样工作。
您案例中的问题是侧边栏即使不可见,也仍然占用实际空间。如果将其应用于更复杂的布局(或者即使侧边栏在左侧),您最终可能会在页面中出现一些空白区域。
您可以修复它,但是然后您将不得不修复填充,并且可能还必须修复各个元素。
@Kyrodes Chris创建的示例中侧边栏最大的问题是
.print-me
类位于浮动元素的子元素上,而不是浮动元素本身。为了使该子元素全宽,您真的需要进入hack领域,设置width: 100vw
,可能还需要使用负边距。如果
.print-me
类位于浮动元素上,则可以简单地添加.print-me { float: none; width: 100%; }
并完成。我已更新了演示来说明这一点。还有
screen
媒体类型,在打印时不会使用它,因此您只能为屏幕启用某些元素为了使其与旧浏览器兼容,您可能可以将
display:none
包装在only all
中这就是我在Shower主题中所做的:我只是包装所有不需要打印的代码到
screen
中+1,这是最简洁的解决方案(代码本身完美地描述了自身,实际上不需要任何文档)并且在我测试过的所有地方都能正常工作。请注意,虽然某些设备/平台已实现其他非打印@media类型,例如“handheld”和“projector”,但据我所知,没有一个在不也解析为“screen”(出于这个原因,我猜想)。
PS,您不需要像您的第二个代码块那样做任何事情来支持旧浏览器——基本@media类型在媒体查询出现很久之前就已经在各处实现了。
我敢肯定
handheld
和projector
已弃用。目前仅允许all
、screen
、print
和speech
。IE8不支持
@media
,只是<link rel="stylesheet" media="print" href="/print.css" type="text/css" />
如何向“.fake-print-state *”添加“width: initial;”?
除非我错了,否则块级元素将占用整个宽度。
如果您愿意,您甚至可以删除浮动。
这是一个示例:http://codepen.io/TheDutchCoder/pen/GJGQLd
我建议看看jxnblk的ReadRead。
在我设计的一个网站上,我使用了黑名单方法。但是,页面上有一些元素我想要仅在打印时显示。我有一个.noprint类和一个.printonly类。效果非常好。
您也可以不必担心将实际的
.print-me
类本身应用于任何标记,而只需使用预处理器在需要的地方扩展它。就像这样(在LESS中)
通过扩展类而不是将其塞满整个标记,您可以避开很多上述问题。
您可以使用此策略执行各种很酷的事情。
使用通用选择器作为后代选择器对性能不好并且被认为是不好的做法吗?也就是说,要查看是否应应用
.print-me *
,我们需要遍历每个元素的DOM以查看其任何父元素是否具有.print-me
类。这是一个纯CSS白名单方法。关键是:(1)使用可见性;(2)将打印部分移动到左上角。
请参阅我在Stack Overflow上的回答以获取(少量)更多解释。
我喜欢这样!
但它确实将“白名单”限制为一个列表。因此,我猜演示中使用了ID。
Robert Stewart 使用一个插件提供了一种替代方法
https://github.com/erikzaadi/jQueryPlugins/tree/master/jQuery.printElement/Sample