写在前面

这几天朋友项目中还在使用window.print,结果问题它又来了。我写了个demo模拟遇到问题的场景,先看效果:




Android Studio print 打印 打印不全_自动分页


看过我前一篇关于print的文章的小伙伴都很熟悉这个界面吧,上次截的时候就有这个页眉页脚了,但上次他没有提这个地方。这次发现问题了,就来解决一下。

言归正传,那想去掉这个其实很简单,只要在浏览器设置中把页眉页脚勾掉就可以了:



Android Studio print 打印 打印不全_分页_02


但还是那句话,你可能给每一个用这个项目的客户说,你要在打印之前去浏览器设置里把页眉页脚勾掉吗?

显然是不可能的。所以像上次的背景水印一样,我们还是需要用代码来实现去掉页眉页脚的效果。

发现问题

我们可以用以下代码去掉页眉页脚:

@media print{
	@page{
		margin: 0;
	}
}

但是,这个地方就有问题了。

一页的时候还看不太出来,两页以上的话你会发现,因为是用伪类@page的外边距margin来去除页眉页脚的,这让每一页的内容都占满了整个页面,也就是:



Android Studio print 打印 打印不全_分页_03


可能光是背景图看不太出来,我用循环在页面上填充了4500个“的”:

for(var i = 0;i < 4500;i++){
	document.getElementsByClassName('container')[0].innerHTML += '的';
}

现在页面是这样的(有点眼花。。但这样填充页面是最方便的,为了下面测断开处的效果):



Android Studio print 打印 打印不全_css_04


可以看到,在自动分页时,超出一页的部分会断开,但因为上面把margin设置为0了,每页的内容都是贴着边的。用过打印机的都知道,打印时它会留一个白边,如果你的页面内容贴着边,就会被遮挡,像这样:



Android Studio print 打印 打印不全_页眉_05


我只拍了左边,上边右边下边如果有遮挡也是这样的。这显然不是我们要的效果。

于是我想到,那我给它的body加个margin值让它不贴边不就好了:

body{
        margin: 20px;
      }

但是预览的时候我发现第一页是可以的,第二页没有用。因为自动分页之后的第二页还是和第一页同一个body。也可以说,他们是同一个body,不同的page:



Android Studio print 打印 打印不全_分页_06


可是对于page的话,我之前用伪类@page已经设置了margin为0了,如果再改变margin值就没有任何意义了。于是我想到,既然margin不行,我就用padding好了。

但很可惜的是,对于伪类@page来说,它的文档上是这样写的:

Android Studio print 打印 打印不全_自动分页_07


翻译过来就是说:@page 规则用于在打印文档时修改某些CSS属性。你不能用@page规则来修改所有的CSS属性,而是只能修改margin,orphans,widow 和 page breaks of the document。对其他属性的修改是无效的。

这告诉我padding也不行。那在只能针对每个page下手,在page只能修改margin的情况下,我只能从margin上做文章,来实现我想要的效果。

解决方案

于是我就试着直接在给@page的margin值时,不给0,而是给一个定值。

@media print{
	@page{
		margin: 22px 10px 16px;
	}
}

意外的发现竟然是可行的,不仅没有页眉页脚了,页面内容也不贴边了:

Android Studio print 打印 打印不全_css_08


什么原理呢?我又多试了几个值,结果发现当大于比如30px的时候,页眉页脚还是会出来,说明并不是页眉页脚没了,而是打印的时候,浏览器自动加的页眉页脚也留出了打印机的那个空白边。于是我猜测,只要我留出的空白和浏览器自动加的页眉页脚时留出的空白一样多,那么不仅不会出现页面打印不全被遮挡的问题,也不会出现页眉页脚了。

这是一个比较笨的办法,但确实也是可行的。上文代码中的margin值是我打印了6次浪费了12张纸后得到的一个既不会遮挡,也不会出现页眉页脚的值。但并不是最精确的,还可以继续调整,但我就不继续测了。

为了防止由于我填充页面的时候全用的是同一个字,页面有遮挡但我没看出来的情况出现,我又在循环里加了指定的两个字:

for(var i = 0;i < 4500;i++){
	if(i == 2396){
		document.getElementsByClassName('container')[0].innerHTML += '一'
	} else if(i == 2397){
		document.getElementsByClassName('container')[0].innerHTML += '二'
	} else {
		document.getElementsByClassName('container')[0].innerHTML += '的'
	}
}

现在页面变成这样:

Android Studio print 打印 打印不全_页眉_09


打出来也是这样的,没有被遮挡的地方。(注意光看预览效果是看不到被遮挡的,一定要去打出来看):


Android Studio print 打印 打印不全_前端_10


小结

window.print使用起来还是有不少问题的,我碰到的多是因为页面超出一页自动分页之后,由于无法对每个页面处理,每次处理的时候要么就是整个body,所以很多问题不好解决。

有的小伙伴可能会问我为什么不用page breaks手动分页,但是我实际用在项目中的时候,一页有多少内容,一共有多少页,都是不固定的,而且有的内容还嵌套在表格里,还有表格被撑开,表格从中间断开等问题,这就更不好用page breaks来处理了。当然,想这样处理也是可以的,只要先把表格渲染在页面上,但不显示,计算好一共要多少页面,每个页面什么地方断开,每个表格多高,什么地方断开,然后再用新的盒子把内容放进去显示,再添加page breaks来分页,这样应该也行,但是挺麻烦的。

只是这个项目因为不是我的,我只是帮忙解决遇到的问题,不好对整个项目修改太多。我也和人家讲了这个思路,人家是否采纳就是后话了。

浏览器兼容性的问题,有不少大佬都写了博客,我这边就不再啰嗦了。上述代码chrome可用,360可用,没测其他。其他浏览器特别是IE怎么处理,大家直接看大佬的文章就可以了。