JeffreyFrancesco.org 公開日 タグ Tag Permalink 現在地 Facebook page Twitter RSS feed

お分かりの方には何てことないというか今さらな話ですが、割と出来なくて悩んでる方が多いように見受けられたので。Twitter のツイートを検索してみてもたまに話題に上がってるっぽいし。

「画像にキャプションを表示する」などの名目でこの img:after { content: attr(alt) } のような[1] CSS 例を挙げているところがいくつかあるのですが、実際にこのような CSS を書いても alt 属性値が画像の後ろに表示される事はありません。Opera では表示されるようですが(あと私の記憶にはないのですが、どうやら古い Mozilla もこのような実装になっていたみたい[2]です)他のモダンブラウザでは基本的に全滅です。

かといって、これは Opera の :before / :after 疑似要素の実装が先んじているという訳ではなく、HTML / CSS 双方の仕様をよく考えれば表示されなくても不思議ではないというか、その方が正しい挙動ですよ…という話。

そもそも :before / :after 疑似要素とは

ある要素に :before / :after 疑似要素と content プロパティを使って生成される内容は、その要素自体の前または後に挿入されるのではなく、その要素の内容の最前もしくは最後に挿入されるという仕組みになっています。例えば <p><span>foo</span></p> というマークアップがあった場合に、

span:after {
    content: "bar";
}

という CSS を適用した場合、bar が挿入される位置をイメージ的にいえば、

<p>
    <span>
        "foo"
    </span>
    "bar"
</p>

ではなくて、

<p>
    <span>
        "foo"
        "bar"
    </span>
</p>

であるということになります。

img 要素は空要素である

次に img 要素を考えてみます。img 要素は HTML においては <img src="hoge" …>XHTML では <img src="hoge" … /> と記述しますが、これはどちらも `<img src="hoge" …></img> と書くのと同義でありまして。HTML は終了タグの場所が自明の場合は終了タグを省略出来ますし、XHTML の場合は終了タグは必須ですがどちらにしても間に内容を一切含める事が出来ないので、短縮して(同時に過去のブラウザとの互換性も考慮して)先ほどのように書く訳です。このように内容を含められない要素を「空要素」といいます…厳密にいえば少し説明が間違ってたり足りないかもしれませんが、何となくそういう感じ。汗

で、今度は <p><img src="hoge" /></p> というマークアップがあるとします。これを先の説明もふまえて考えると <p><img src="hoge"></img></p> となるのはお分かりいただけるのではと思うのですが、ではこれに対して、

img:after {
    content: "baz";
}

という CSS を適用した場合に、baz が挿入されるのはどこでしょう?そうですね。

<p>
    <img src="hoge">
        "baz"
    </img>
</p>

となりまして(あくまでもイメージ上・理屈上の話です)決して img 要素の終了タグの後に baz が続く訳ではないですよ、というのはお分かりいただけるかと。

また先に申し上げた通り img 要素の内容には何も含める事が出来ませんから、この記述自体エラーとして処理されてしまいますし、百歩譲ってエラーではないとしても、ブラウザでレンダリングされる際には img 要素そのもの…つまり開始タグから終了タグまで…が画像に置換される訳なので、決して baz が表に目に見える形で現れてくる事はないという、そういう理屈です[3]

以上で、仕様通りに解釈すれば img:after { content: attr(alt); } などがブラウザ上に何かを表示する事はない、というのがお分かりいただけたのではないでしょうか。まあ確かにこう書いて alt 属性や title 属性の値を表示出来た方が色々と便利なのかもしれませんが、Mozilla や Webkit がその辺りを仕様通りに実装している以上それは望めないので、仕方がないですねえ…というのが正直なところですね。

ちなみに何故 Opera ではこの記述が有効なのか?については私は知りません。Opera の中の人に訊いて下さい。汗

CSS3 ではどうなるか

ちなみに CSS3 においては ::outside という、要素の外側をラップする疑似要素が定められる予定(これを書いている時点ではまだワーキングドラフトなので実際どうなるか分かりませんし、どのブラウザでも現時点では実装されてないのが現状ですが)なので、もし上手くいけば以降はこういう CSS を書く事が可能になるかもしれません。

img::outside {
    display: block;
}
img::outside::after {
    content: attr(alt);
}

img 要素の外側に要素を生成して、その外側の要素の最後に alt 属性の値を内容とした要素を追加する感じ。

これなら img 要素の後にキャプション的なものを追加するという事も img 要素だけで一応は可能になりそうです…が、そんな事をするくらいであれば最初から普通に HTML だけで、

<figure>
    <img src="hoge" />
    <figcaption>fugafuga</figcaption>
</figure>

と書いてしまった方が良いし、その方がむしろ適切なような気が私はしてますけどもね。

という訳で、今回は途中まで書いたエントリの文章が Coda のエラーで落ちて消えてしまったために、半泣きでお送りしました。涙 やっぱりこまめにセーブはしておくべきなのだなあ…

本エントリに関連する参考記事

空要素に対するCSSの疑似要素(:before, :after)指定 - rikubaの日記

  1. alt 属性よりも title 属性の方が画像のキャプションとして表示するには適切なのでは?とかいう辺りの論議は今回はとりあえず横に置いておきます。汗 一例として読んで下さい。 ↩

  2. float: による文字の回り込みと clear / スタイルシート具体例 / 詰め CSSの下の方に、このような CSS を書いたのを当時の Mozilla 0.9.7+ で表示した際のキャプチャ画像があるので、そうだったのかなあっていう。自身が確認した訳ではありません。 ↩

  3. 実際に XHTML 文書の中で <img …>fuga</img> と書くと画像の後ろに fuga が表示されるのですが、それは単にブラウザが「これはきっと <img … />fuga</img> の記述間違いに違いない」と解釈してそう表示しているからで、ここで書いている話とはまた別の話なので、それを理由に突っ込まれてもちょっと困ります。汗 実際この記述は valid ではないですし。 ↩