印刷用の CSS と jQuery プラグイン

URL のリストを出力するスクリプトを一部修正して Gist に置きました

このサイトの印刷時のスタイルというものをまったく考えていなかったことに気づいたので、あわてて CSS と jQuery プラグインを書いて対応した。テストするのに結構な量の紙を消費したけど、サイトを見てる人には印刷してもらわない限り工夫が伝わんないってのがなんかもったいないとか思ったので、やったことをまとめて記事にしてみる。

CSS ファイル

印刷専用の CSS ファイルは用意せず、汎用 CSS ファイルの末尾に @media ルールで記述している。サイトの規模や運用方法なんかにもよるだろうけど、ファイル数はなるべく減らしたいので。

不要な要素を削除

CSS ではまずは印刷物として必要のない要素を display: none; で削除。各種ナビゲーション、ボタン、関連記事のリスト、サイドバー (検索、タグ・クラウドなど) あたりをばっさり消した。

表示エリアの幅

スクリーンでのレイアウトは左側に大きくパディングをとってるけど、印刷時は本文部分を中心に widthauto、左右の paddingmargin0 にして、表示領域をなるべく広く確保。

タイポグラフィ

ロゴや見出しに Web フォントを使ってるけど、同じフォントがローカルにも入ってないと印刷には反映されない、というのは盲点だった。そのためロゴは Helvetica/Arial を使った印刷専用のスタイルに大幅修正。邪魔にならないサイズに抑えつつ、それだけだとちょっとさみしいので Favicon 画像を content プロパティで挿入してみた。

.logo {
  font: 16px/1 "Helvetica Neue", "Helvetica", "Arial", sans-serif;
}
.logo:before {
  content: url(/img/favicon.png);
  margin: 0 8px 0 0;
  float: left;
}

本文部分のフォントフェイスはメイリオで、サイズは 10px、行間はスクリーンよりやや大きく。メイリオはスクリーンで ClearType が無効の場合にかなり汚いので使うのがためらわれるけど、印刷だと小さいサイズでも読みやすいしボールドがしっかりボールドになるしでけっこうおすすめ。背景色と前景色はモノクロ、でもリンクやコードなどの配色はスクリーンのまま。

body {
  font: 10px/1.8 "Meiryo", "Lucida Grande", "Verdana", sans-serif;
  background: white;
  color: black;
}

リンクの URL をリストアップ

言うまでもなく、印刷されたドキュメントはハイパーリンクの機能を持たないので、なんらかの手段でリンク先の URL を表現したいところ。よく用いられるのは、CSS の content プロパティで href 属性の値を出力する方法:

.content-body a:after {
  content: " (" attr(href) ")";
}

詳しくは A List Apart: Articles: CSS Design: Going to Print などを参照のこと。スマートだけど、とくに長い URL の場合に文章のリズムがくずれて読みにくくなってしまうのが難点。そこで今回は、リンクの URL を抽出して記事のおわりにリストアップする jQuery プラグインを書いてみた:

(function ($) {
  $.fn.printHrefList = function () {
    return this.each(function () {
      if (!$(this).is(':has(a[href])')) {
        return;
      }
      var $this = $(this),
        $hrefList = $('<dl class="href-list" />').appendTo(this).hide(),
        urlArr = [];
      $this.find('a[href]').each(function () {
        var $this = $(this),
          url = this.href,
          index;
        if ($.inArray(url, urlArr) < 0) {
          urlArr.push(url);
          index = urlArr.length;
          $hrefList.append('<dt>' + index + '</dt><dd>' + url + '</dd>');
        } else {
          index = $.inArray(url, urlArr) + 1;
        }
        $this.after(
          $('<sub class="href-index">[' + index + ']</sub>').hide()
        );
      });
    });
  };
})(jQuery);

なにをやってるかというと、

  1. 対象要素の末尾に dl 要素を生成し、いったん非表示に
  2. 対象要素内の a 要素を見つけて、それぞれのうしろに sub 要素を生成して連番をふり、やはりいったん非表示
  3. a 要素の番号を dt に入れて dl に追加
  4. 同じく a 要素の href 属性値を dd に入れて dl に追加

という流れ。たとえば .content-body 内のリンクを抽出したいなら、まずこうしてメソッドを呼び出す:

$(function () {
  $('.content-body').printHrefList();
});

そして、リスト (dl) とリンクの番号 (sub) が .hide() で非表示になっているので、印刷時のみ表示されるようにスタイルを指定する:

@media print {
  sub.href-index {
    display: inline !important;
  }
  dl.href-list {
    display: block !important;
  }
}

Fig 3: スクリプトと CSS 適用後の印刷プレヴュー

同じ URL が複数出現した場合にちょっとうざいかなと思ったけど、対応するとややこしくなりそうなので保留。同じ URL が複数出現した場合に繰り返し出力しないよう修正した。