input要素のplaceholder属性で指定することができるプレースホルダー文字列が場合によって位置がずれることがあることを知った。どうもinput要素へフォント指定があり、かつプレースホルダー文字列にマルチバイト文字列が含まれていると再現するようだ。

Demo: Placeholder Position on Chrome 47

再現環境はChrome 47とEdge 12ということで、OSには依存しない(が、ブラウザーのフォント設定によっては再現しない)。デモ・ページでは下に少しずれているため、プレースホルダー文字列が入力ボックスの天地中央にきていない。加えて文字“p”のディセンダーが欠けている。

Chrome 49でも再現したので、すぐに直ることはなさそうだ。このバグを報告したイシューは立てておいたが、既にある178032なのかもしれない(とすると3年ほど前からあるのでどうも直りそうもないことになる)。


直るのを待つのは厳しそうなので、小手先で対応する必要がありそうだ。原因はShadow DOMとして追加されるプレースホルダー文字列のための要素がdisplay: blockになっていることが問題なのではないかと想像している。そのためブラウザーが想定する行ボックスの高さがずれ、うまく揃わないのではないだろうか。これをうまく変えられれば話は速いのだが、インライン・スタイルである上に当然のように!importantが鎮座しているためこれを上書きするのは難しい。

そこでマジック・ナンバーを使わずに行ボックスを調整する方向で考えてみる。

::-webkit-input-placeholder {
  line-height: 1;
}

こうすると欠けることはなくなるが、今度は微妙に上にずれる。これでも悪くはないが、それならいっそ行ボックスがブラウザーの想定通りになるようにしてやるという方が確実か。

input {
  font-family: initial;
}

このようにしてフォント指定をリセットすることになる。Internet Explorer 11では再現しないので、initialキーワードを使っても良いはずだ。ただし影響はかなり大きいので採用するにはもうちょっとテストを重ねる必要がありそうだ。


本バグとは関係ないが、どうやらChrome 47では既に(ようやく?):placeholder-shown擬似クラスに対応しているようだ。いつからだろう。Can I Use...ではまったく対応されていないことになっていて気付かなかった。どうやってPRするんだっけ……。

追記

input {
  font-family: inherit;
  line-height: 100%;
}

このプレースホルダー文字列の位置ずれに対しては、こうしてフォントを揃えた上でline-heightプロパティーで制御する方が良さそうな感じだった。弊害もなく、多分ないだろうと想像することができる(子に伝播することがないため)。

:placeholder-shown擬似クラスについては挙動がかなり謎だった。しかし開発者ツールからプリフィックスなしのものが見え、一部のCSSプロパティーを適用することが可能なので、部分的な実装はあるとみなして良さそうだ。

追記 #2

イシュー立てたところすぐに反応があり、修正が入った。どうやらChrome 50で直るようだ。既にCanaryで直っていることを確認できる。