knowledge base

マークアップ/フロントエンドエンジニアのWEB制作における備忘録です。平日はWEB屋、休日は社会人劇団の主宰・劇作家をしています。

ブラウザで表示できる最小フォントサイズ(10px)以下の文字を表示する

ブラウザが表示できる最小フォントサイズは10px

ブラウザは可読性維持のために、CSSで10px未満のフォントサイズを指定しても無視するという機能が備わっています。 以下の例では、決して7pxで表示されることはありません。 後述しますが、zoomを使っても無駄でした。

p {
  font-size: 7px; /* 10pxで表示されてしまう */
}

下記事例はとある図表の一部なのですが、見出しエリアを一行分しか確保していなかったため、あふれてしまいました。

 

f:id:ShinImae:20160108174635j:plain

これは英語を基準にデザインされていたコンテンツを他言語対応する際に起きた問題です。

ロシア語など文字量の多い言語では改行が必要になってしまい、見出し用に確保したエリアからあふれてしまいました。

諸事情によりHTMLを編集することができないため、見出し部分を画像に置き換えることもできませんでした。

そこで文字サイズを小さくすることで対応しようとしたのですが、10px以下の文字サイズでないとタイル状に並べた画像エリアに重なってしまうことが分かりました。

ちなみには、本来想定していたもの(一行の見出し)は次のようなデザインです。

f:id:ShinImae:20160108174648j:plain

先日印刷プレビューについて記事を書かせて頂いたときと同様、ユーザー側の設定で最小文字サイズを変更することができますが、デザインの都合上どうしても極小の文字サイズで表示したい場合があります。

今までは画像に頼らざるを得ませんでしたが、モダンブラウザが普及しつつある最近であれば画像に頼らなくとも実装することができます。

transform:scale を使う

モダンブラウザ(IE9+, GoogleChrome, FireFox, Safari, Opera)であれば、CSS3のプロパティであるtransformを用いて拡縮します。

h2 {
   -moz-transform: scale(0.8);
   -webkit-transform: scale(0.8);
   -o-transform: scale(0.8);
   -ms-transform: scale(0.8);
   transform: scale(0.8);
}

scaleの引数には倍率を指定します。今回は0.8倍になるように指定しています。

ただしこれらの指定のみでは次のようになってしまいます。

f:id:ShinImae:20160108174658j:plain

薄く塗られた領域分だけ、ずれてしまいました。

これは、変形する際の基準点が要素の中心に設定されているためです。

そこで、変形の基準点を左上に変更するため、次のプロパティも変更します。

h2 {
   -moz-transform: scale(0.8);
   -webkit-transform: scale(0.8);
   -o-transform: scale(0.8);
   -ms-transform: scale(0.8);
   transform: scale(0.8);
   -moz-transform-origin: 0px 0px;
   -webkit-transform-origin: 0px 0px;
   -o-transform-origin: 0px 0px;
   -ms-transform-origin: 0px 0px;
   transform-origin: 0px 0px;
}

transform-originは50% 50%がブラウザの持つ初期値です。

基準点の指定方法については、左上を基点として算出します。

background-positionと算出方法は同じです。

こうすることで、意図した表示を実装することができました。 

f:id:ShinImae:20160108174709j:plain

レガシーなIEはzoomを用いたほうが早い

実はIE8以下には、filterプロパティにより独自拡張機能で透過・フィルター・シャドウ・変形などのCSS3ライクな機能がすでに実装されています。

下記のようなものを一度は見たことがあるのではないでしょうか。

filter:progid:DXImageTransform.Microsoft...

www.htmq.com

これにMatrixを渡して拡縮させることは可能なのですが、算出方法がたいへん難解なのでお勧めしません。

コンバートするサイトもあるようですが、むしろ直感的に分かりやすいzoomを指定したほうがよいと個人的には思います。

レガシーなIEならばzoomで文字サイズを拡縮させることが可能だからです。

/* レガシーなIEにのみ効くようなクラスを追加 */
h2.ie-fix {
   zoom: 0.8; 
}

多少サイズ感が変わってしまうので、高さなどを別途調整すれば、CSS3を用いた場合と同様の表示を実装することが可能です。

Androidデフォルトブラウザでviewportが無視される

たとえば下記のようなページを想定します。

 

f:id:ShinImae:20160108163451j:plain

ビジュアルが表示幅ぴったりに広がっており、とても洗練されたデザインですね。

今回はこのデザインを横幅320pxに最適化して表示する際の問題について書きたいと思います。

横幅320pxに最適化とは

横幅320px固定のデザインを、各デバイスの幅に合わせてフィットさせることを言います。

具体的には次の指定をすることで最適化を行います。

<meta name="viewport" content="width=320" />

この指定により、横幅320px固定でコーディングしても、各デバイスの幅にぴったりフィットします。

ところが、デバイス幅が320px以上のAndroid端末(デフォルトブラウザ)で表示すると下図のようにviewportの指定が無視されてしまい、ページ両端に余白が生じてしまいます。

f:id:ShinImae:20160108163458j:plain

 

どうやらAndroidデフォルトブラウザのもつバグのようで、AndroidGoogleChromeFirefoxでは、デバイスの横幅にぴったりフィットします。

この問題を解決するには2段階の方法をとります。

  1. Androidデフォルトブラウザのみを判別できるようにする
  2. コンテンツがデバイスの横幅にフィットするように拡大するような指定をかける

では、ひとつづつ解決法を見てゆきましょう。

Androidデフォルトブラウザの判別

あまり知られてはいませんが、IE,Chrome,FireFoxと同様にデフォルトブラウザもUserAgentから判別できます。

こちらの記事で詳しく解説されているとおり、どうやら「linux; u;」という文字列が含まれていればデフォルトブラウザであるようです。

yoyogisan.hatenablog.com

まずは下記方法で、デフォルトブラウザを判別します。

var ua = window.navigator.userAgent.toLowerCase();
if(ua.indexOf('linux; u;') != -1){
    return true;
}

より確実に判別したいなら、OSがAndroidであるか判別した上で、デフォルトブラウザであるかどうかを判定します。

var ua = window.navigator.userAgent.toLowerCase();
if(ua.indexOf('android') != -1){						
   if(ua.indexOf('linux; u;') != -1){
      return true;
   }
}

横幅にフィットするように拡縮指定をする

横幅320pxに対する現在のウィンドウ幅の比率を算出し、その比率だけ拡縮するようにします。ロジックは次のとおり。

var windowWidth = $(window).width();
var ratio = windowWidth/320;
$('html').css({'zoom' : ratio });

今回はウィンドウの横幅を320で除算していますが、viewportのサイズによってこの値は変化します(たとえば640pxで最適化をするなら640で除算します)。

こうすることで、小さなデバイスでは縮小され、大きなデバイスでは拡大されます。

これを、先ほどご紹介したデフォルトブラウザ判別方法に組み込みます。

var ua = window.navigator.userAgent.toLowerCase();
var windowWidth = $(window).width();
var ratio = windowWidth/320;
if(ua.indexOf('android') != -1){						
   if(ua.indexOf('linux; u;') != -1){
      $('html').css({'zoom' : ratio });
   }
}

こうすることで、デフォルトブラウザでもねらい通りの表示を実現することができます。

text-indentも指定してもテキストを非表示にできない

jQueryプラグインを利用してスライダーを作っていました。

f:id:ShinImae:20160108161745j:plain

どうやらこのプラグインは、サムネイルではなく番号をコントローラーとして出力するようです。

ところがデザインでは、この部分には番号ではなく丸いボタンが指定されていました。

f:id:ShinImae:20160108161818j:plain

なので、背景色や角丸をCSSで設定し、番号を飛ばすことにしました。

プラグインの中身をカスタマイズする方法もあるのですが、文書構造的に番号をソースに残したかったのでCSSのみで対応することにしました。

f:id:ShinImae:20160108161854j:plain

テキストを非表示にするためによく使われる、以下のスタイルを指定しました。 ポイントとなるのは7行目、text-indentに大きな値を指定する点です。

ul li{
  width: 17px;
  height: 17px;
  background: #FFF;
  border-radius: 50%;
 text-align: right;
  text-indent: -99999px;
}

ところが、テキストが非表示になりません。

f:id:ShinImae:20160108161854j:plain

CSSをよく見て頂ければわかるのですが、実はこのサムネイルにはtext-align:rightも同時に指定されており、これが原因であるようです。

理屈はいたって簡単で、インデントで負の値を指定しても、右揃えになってしまうためテキストの末端が表示されてしまうという仕組みでした。

なので、今回のようにtext-align:rightが指定してある場合は、次のように指定を変えれば解決できます。

text-indent: 99999px;

これで無事に意図した表示を実装することができました。

f:id:ShinImae:20160108161818j:plain

Androidデフォルトブラウザで表示が崩れる

ところがこのスタイルをAndroidデフォルトブラウザで表示すると、次のような表示になってしまいました。 

f:id:ShinImae:20160108161933j:plain

どうやらAndroid端末ではtext-indentで指定した分だけ、描画領域を確保してしまうようです。

ほかにもAndroidではtext-indentが効かないというバグが頻繁に効かれます。

そこで、Androidにも対応できるスタイル指定を行います。

※下記CSSではサイズや背景色の指定は省略しています。

ul li{
  text-indent: 100%;
  white-space: nowrap;
  overflow: hidden;
}

text-indent:99999pxの代わりに上記のスタイルを3つセットで指定すると、きちんとテキストを非表示にすることができます。

f:id:ShinImae:20160108162028j:plain

こちらの指定、もちろんPCやiOSでも有効です。

なのでレスポンシブコーディングや、マルチデバイス対応の際は、この指定を使えばたいていのデバイスでテキストを非表示にすることができます。

IE8で明朝体の表示が崩れる

明朝体にするとき、最も簡単なフォント指定は次のとおりです。

font-family: serif;

これでほとんどのブラウザでは明朝体になるのですが、IE8だけ表示が崩れてしまいます。下図の赤枠で囲った文字がその顕著な例です。

f:id:ShinImae:20160107214023j:plain

「青」がゴシック体になっているし、「飾」も明朝体ではあるものの見慣れない字体になっています。
調べたところ、どうやらIE8はきちんとフォント名まで記述しないといけないようです。

font-family: 'MS P明朝', 'MS PMincho', serif;

これで狙い通りのフォントで表示することができます。

関数内でその関数自身を取得実行する

関数内で、その関数自身の情報を得たいなと思っていました。

次の方法で可能となります。

var functionA = function(){
   console.log(arguments);
}

argumentsというオブジェクトは、本来名前の通り関数に渡された引数を参照できる配列に似たものです。

もし関数に3つの引数が渡されたなら、arguments[0], arguments[1], arguments[2]という具合に参照ができます。

arguments.callee

さて、argumentsにはlengthとcalleeというプロパティがあり、lengthは引数の数、calleeは現在実行している関数の関数本体を示します。

今回用いるのは、まさしくこのarguments.calleeというプロパティです。

このarguments.calleeにはいくつかプロパティがあり、関数名などを取得することができます。

var functionA = function(){    
   console.log(arguments.callee); /* 関数そのものを出力 */
   console.log(arguments.callee.name); /* 関数名(functionA)を出力 */
}

arguments.calleeは関数本体を参照するため、呼び出すことが可能です。

たとえば、無名関数内で再帰処理をしなければいけない時などに利用できます。

ちなみに下記コードは再帰処理を終わらせるための分岐を書いていないので、無限ループになります。あしからず。

function(){
  /* 関数名を与えなくても再帰呼び出しできる */ arguments.callee(); }

また、自分自身をイベントにon/offすることもできます。

下記例ではmyEventというカスタムイベントに一度関数を紐づけたのち、その関数実行後にイベントとの紐づけを解除しています。

myEventというイベントは何度も起こりえますが、それに紐づけた関数onMyEventFuncは一度のみの実行でよい場合に使えます。 (※ちなみにカスタムイベント作成のプログラムは省略しています)

var onMyEventFunc = function(){
    $(window).off('myEvent', arguments.callee );
}
$(window).on('myEvent', onMyEventFunc );

余談

しかしながら、strictモードの際にこのプロパティを参照するとエラーになるそうです。

紹介した割に、最近は非推奨になっているという悲しい結末で申し訳ありません。

どうやら引数に対して操作を行えるということが、あまりよろしくないそうです。

strictモードでは、関数名を与えたり、引数を別の変数で受け取る等の代替案をとらなければいけません。

JavaScriptを用いずにクリックを無効にする

クリックを無効にするには、JavaScriptを用いる手法をよく見かけますが、CSSでも実装が可能です。

a{
  pointer-events: none;
}

対応ブラウザはモダンブラウザが中心となっているそうです。

なぜこのようなプロパティがあるのだろうと思ったのですが、どうやらSVGと併用するといろいろなことが実現できるそうです。

以下で解説されているように、SVGのためのプロパティがいくつか用意されています。

developer.mozilla.org

背景色や背景画像も印刷したい

ブラウザのもつ印刷機能では、デフォルトで背景色や背景画像を印刷しないことになっています。

画像をHTML上に直書きするのも一つの手段ですが、セマンティックなHTMLドキュメントを書こうとするとどうしても限界があります。

IEの各バージョン / Firefox / Opera ではブラウザ側の設定で印刷が可能ですが、ユーザー側の設定に関わらず必ず印刷させたい場合があります。

そんなときは、webkit系でのみ下記スタイル指定によって可能となります。

@media print {
   body {
      -webkit-print-color-adjust: exact;
   }
}