knowledge base

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

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;
   }
}

FireFoxでinline-blockがカラム落ちする

inline-blockとletter-spacingを同時指定すると崩れる

たとえば下図のようなレイアウトを実装すると想定します。

f:id:ShinImae:20160107205014j:plain

リストを横並びにした、シンプルなレイアウトです。

これをinline-blockを用いて横並びにすると、なぜかFireFoxだけカラム落ちしてしまいました。

f:id:ShinImae:20160107205029j:plain

実はこれ、inline-block間の隙間を削除するためにlette-spacingで調整をしています。

HTMLは下記の通りです。

<ul class="menu">
    <li class="menu__item">Nav Lorem</li>
    <li class="menu__item">Nav Lorem</li>
    <li class="menu__item">Nav Lorem</li>
    <li class="menu__item">Nav Lorem</li>
</ul>

そして、CSSは下記の通りです。

.menu{
    letter-spacing: -0.4em;
}
  .menu__item{
      display: inline-block;
      letter-spacing: normal;
  }

inline-blockを横並びにすると、改行コードが原因で存在しないはずの隙間が生まれてしまいます。

そのため、letter-spacingを利用して要素同士を隙間なく接するようにするテクニックが存在します。

ところがinline-blockのみの指定では崩れず、どうやらletter-spacingと併用するとこのようなカラム落ちが発生してしまいます。

ブラウザによってinline-blockの実装方法が違うという説もありますが、実際のところ明確な理由ははっきりしていないそうです。

そこで用いられるのが、word-spacingというプロパティです。

単語の間隔を指定するためのプロパティなのですが、letter-spacingと同時に指定することで、崩れを防ぐことができるようです。

指定するのは0.1emでなくてもよいです。とりあえず正の値を指定すれば崩れることがなくなるようです。

.menu{
    letter-spacing: -0.4em;
    word-spacing: 0.1em;
}
  .menu__item{
      display: inline-block;
      letter-spacing: normal;
  }

もし同時に指定することに抵抗があれば、FireFoxのみ有効なCSSハックを用いて指定すればよいでしょう。

.menu{
    letter-spacing: -0.4em;
}
  .menu__item{
      display: inline-block;
      letter-spacing: normal;
  }
@-moz-document url-prefix() {
  .menu{
      word-spacing: 0.1em;
  }
}

transitionのコールバックを実装する

transitionが終了したら実行されるようにしたい

CSS3のプロパティであるtransitionを用いてアニメーションをさせるとき、transition終了時にコールバックを実装したいときがあります。

jQueryのanimateメソッドならば、簡単にコールバック関数を指定できるのですが、transitionを用いた場合はどのようにコールバックを実装したらよいでしょうか。

スタイルシート上では設定できませんが、JavaScriptにて制御ができます。 transitionendというイベントにバインドさせることで、それが可能です。

下記は、水平方向に300px移動した後にアラートを出力するプログラムです。(ベンダープレフィックスを取得するgetPrefix関数を独自に作成していると仮定します)

/* ベンダープレフィックスを得る */
var prefix = getPrefix();

/* cssメソッドにわたすスタイルを格納 */
var animObj = {};
animObj[prefix + 'transform']  = 'translate3d(300px, 0px, 0px)';
animObj['transition'] = prefix + 'transform 400ms swing';

/* アニメーション */
$(ELEMENT).css(animObj);

/* transition終了時 */
$(ELEMENT).on('transitionend', function(){
   alert('moved');
});

このイベント、ブラウザによって実装が異なるため、より確実にバインドしたいならば、以下のように各ブラウザでのイベント名を列挙してしまったほうが無難です。

webkitTransitionEnd 
MozTransitionEnd 
mozTransitionEnd 
msTransitionEnd 
oTransitionEnd 
transitionEnd 
transitionend

ちなみに、別の文脈において同じ要素に複数transitionが指定されているときは注意が必要です。

他の文脈でtransitionが終了したときも同じ処理が実行されてしまうため、以下のようにコールバック処理の中でいちどアンバインドさせてください。

/* ベンダープレフィックスを得る */
var prefix = getPrefix();

/* cssメソッドにわたすスタイルを格納 */
var animObj = {};
animObj[prefix + 'transform']  = 'translate3d(300px, 0px, 0px)';
animObj['transition'] = prefix + 'transform 400ms swing';

/* アニメーション */
$(ELEMENT).css(animObj);

/* transition終了時 */
$(ELEMENT).on('webkitTransitionEnd MozTransitionEnd mozTransitionEnd msTransitionEnd oTransitionEnd transitionEnd transitionend',function(){
   alert('moved');
   $(this).off('webkitTransitionEnd MozTransitionEnd mozTransitionEnd msTransitionEnd oTransitionEnd transitionEnd transitionend');
});