一个打印页面局部内容的需求
我假设你是一名挺有经验的开发人员,你现在碰到一个这样的需要,你需要在网页编辑区域输入一些基本信息,通过点击打印按钮将你录入的基本信息作为一份清单打印出来。例如,打印一份学生的成绩清单。网页的整体结构有顶层导航栏,左侧菜单栏,以及和菜单栏并列的内容区。你仔细分析了需求,知道菜单栏和导航栏肯定不能输出到打印内容里面。因此,不能将整个页面直接打印,而是打印局部区域,即打印内容取悦。而且作为局部区域,对于打印格式来说,仍然是有要求的,显然像页面元素的输入框,列表框,多行文本域直接打印到纸质页面里那就相当的不专业。因此,你需要获取页面的内容区域的元素内容,将该内容格式化为对于打印格式来说更为友好方式进行输出。在我的印象中,除了51job等一些求职类网页有打印功能,真的很少有网页需要打印功能。网页的出现明明就是为了“无纸化”办公,然而只有你想不到的需求,没有我做不了的功能。如果你在网上查找资料,很容易找到下面一段代码:
function printDeal(){
var printBox = document.getElementById('printBox');
//拿到打印的区域的html内容
var newContent =printBox.innerHTML;
//将旧的页面储存起来,当打印完成后返给给页面。
var oldContent = document.body.innerHTML;
//赋值给body
document.body.innerHTML = newContent;
//执行window.print打印功能
window.print();
// 重新加载页面,以刷新数据。以防打印完之后,页面不能操作的问题
window.location.reload();
document.body.innerHTML = oldContent;
return false;
}
这端代码虽然能满足一般的打印需求。但存在两个问题:1.只能打印静态页面。2.如果这是一个复杂页面,比如页面是通过layout布局将导航栏,菜单栏,内容区整合到一起,这时如果对内容区进行局部打印,那么打印格式将和页面格式存在非常大的差异。关于第二点,可能和layout的布局细节有关系,因时间关系,暂时无法深究,据本人的经验是,内容都打出来了,格式却是一团糟。 那么,有没有什么办法呢?
模板页打印法
在分析基本原理前,我们需要仔细分析上面的代码。很明显,window.print不能传递参数,因此,它自身是不能支持局部打印,只能整个页面打印。以上代码的基本逻辑就是:在页面打印前,将打印区域单独拿出来,对页面内容进行重新赋值,执行完打印后,再将旧的内容替换回去,从而实现局部打印。那么静态页面打印的基本原理是:将一个静态页面做为格式化打印 模板,通过window.open传递location参数将打印内容传递过去,静态页面通过js获得location参数后将内容填充到模板页中,然后打印模板页。以下为一个内容编辑页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
td{
height: 100px;
}
</style>
</head>
<body>
<div>
<table border="0" bordercolor="black" style="text-align: center; height: 219px;" width="100%" cellspacing="0" cellpadding="5">
<tr>
<td colspan="5"><h2><span id="name">吴水金</span>的成绩单</h2></td>
<!--<td>1.2</td>-->
<!--<td>1.3</td>-->
</tr>
<tr>
<td colspan="2"><b>语文:<span id="language">22</span></b></td>
<td colspan="1"></td>
<td colspan="2"><b>英语:<span id="english">22</span></b></td>
</tr>
<tr>
<td colspan="2"><b>数学:<span id="math">22</span></b></td>
<td colspan="1"></td>
<td colspan="2"><b>物理:<span id="physics">22</span></b></td>
</tr>
<tr>
<td colspan="2"><b>历史:<span id="history">22</span></b></td>
<td colspan="1"></td>
<td colspan="2"><b>政治:<span id="policy">22</span></b></td>
</tr>
<tr>
<td colspan="2"><b>音乐:<span id="music">22</span></b></td>
<td colspan="1"></td>
<td colspan="2"><b>体育:<span id="sport"">22</span></b></td>
</tr>
<tr>
<td colspan="5" ><b>综合评价: <span id="des">xxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddxxxxxxxxxxxxx</span></b></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</div>
</body>
</html>
以上为本人做的一个模板页,是一个学生的成绩单,页面样式较为简单,只为演示原理,你完全可以根据需要自行调整。可以看到,上面的需要动态填充的元素都设置了id。这个页面是完全按照打印格式设置的。那么,我们再做一个内容编辑页面,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<table width='100%' border='1' style="text-align: center;" cellpadding="0">
<tr>
<th colspan="2">
<div style="float: right;">
<button onclick="print()">打印</button>
</div>
<div>导航栏</div>
</th>
</tr>
<tr>
<td rowspan="2" style="width: 20%;">
<div style="height: 500px;">菜单栏</div>
</td>
<td colspan="2">
<table width='100%' border='0' cellpadding='30px' cellpadding="0">
<tr>
<th colspan="2">姓名: <input id="name" value="周杰伦"/></th>
</tr>
<tr>
<td>语文: <input id="language" value="100"/></td>
<td>英语: <input id="english" value="88"/></td>
</tr>
<tr>
<td>数学: <input id="math" value="89"/></td>
<td>物理: <input id="physics" value="89"/></td>
</tr>
<tr>
<td>历史: <input id="history" value="89"/></td>
<td>政治: <input id="policy" value="56"/></td>
</tr>
<tr>
<td>音乐: <input id="music" value="89"/></td>
<td>体育: <input id="sport" value="89"/></td>
</tr>
<tr>
<td colspan="2">综合评价: <textarea id="des" rows="5" cols="100" style="vertical-align: top;">周杰伦是个好孩子</textarea></td>
</tr>
</table>
</td>
</tr>
</table>
<iframe id="printf" src="./model/index.html" width="0" height="0" frameborder="0"></iframe>
</body>
</html>
这是一个成绩单的编辑页面,这里模拟了一个layout的布局模式,即经典的导航栏,菜单栏,内容区的布局模式。为了达到只打印内容区,并且打印格式必须按打印格式进行格式化输出的效果。我们希望将内容区的编辑内容传递到模板页面,模板页填充内容后执行打印。那么,我们在编辑页面的head加入如下代码:
<script>
function print() {
//获取元素值
let name = document.getElementById('name').value;
let language = document.getElementById('language').value;
let english = document.getElementById('english').value;
let math = document.getElementById('math').value;
let physics = document.getElementById('physics').value;
let history = document.getElementById('history').value;
let policy = document.getElementById('policy').value;
let music = document.getElementById('music').value;
let sport = document.getElementById('sport').value;
let des = document.getElementById('des').innerHTML;
//用模板字符串拼接请求。注意模板页路径,根据实际情况修改。
let location = `./model/index.html?${'name=' + name}
${'&english=' + english}
${'&language=' + language}
${'&math=' + math}
${'&physics=' + physics}
${'&history=' + history}
${'&policy=' + policy}
${'&music=' + music}
${'&sport=' + sport}
${'&des=' + des}
`;
//打开新窗口并传递打印内容
const win = window.open(encodeURI(location),
'newwindow',
'height=0, width=400, top=100, left=100, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no'
);
win.print();
}
</script>
以上代码逻辑非常简单,值得注意的模板字符串的写法,这是ES6标准的新写法,还是非常好用的。另外一点值得注意的是这里是通过window.open传递查询字符串将内容传递到静态模板页中。其他参数无关紧要。win.print没什么好说的,就是打印模板页。这边的内容传递过去了,那么,现在需要在模板页中接收,并且将接收的内容填充的模板页中的待填充区域。即上面的已经预留好的带id的模板页元素。在模板页中加入以下代码,加在body元素的末尾:
<script>
//循环遍历进行赋值
let params = decodeURI(window.location.search).substr(1).split('&');
for(let p of params){
let keyValue = p.split('=');
document.getElementById(keyValue[0]).innerHTML = keyValue[1];
}
</script>
window.location.search为查询字符串,我们对它进行切割,即能找出对应的字段和值。由于我们定义参数时即有意将参数名和模板页元素id进行一一对应。因此,这里我们只需要一个循环就能对模板页中所有待填充元素进行赋值。
好了,到此为止,所有代码都完成了。如果你ctrl+c,ctrl+v大法用的比较熟练,想必已经实现打印效果了。尽管已经实现了打印,但该打印仍有不足的地方:1.打印时会弹出一个多余的模板页页面。2.该打印没有走后台系统,也就是说这些打印内容没有提交后台保存即进行了打印,因此如果编辑内容没有提交保存就打印,就会出现后台没有任何记录的纸质打印清单。对于第二点,这是业务逻辑方面需要考虑的,和纯粹的打印功能无关。关于第一点,本人还想介绍下iframe打印大法,但由于篇幅所限,另外,本人亲身试验,iframe传值有问题,就不再这里介绍了。
好了,就到这里吧,我要睡觉了,希望你有所收获。
网友评论