えちょ記

語らないブログ

webさくらの技術:cssセレクタで書く単語辞書

webさくらのSHIORI「ranka(乱歌)」では、辞書をhtmlで直接記述するという、斬新だか無茶だかよく分からない手法を用いています。今回のwebさくらの辞書は、華和梨で記述された「.さくら」の辞書を乱歌用にコンバートしています。単語辞書はほとんど華和梨の考え方を踏襲していますが、会話辞書に関しては私の思うところにより、かなり考え方が異なります。
さて、今回はとりあえず、基本となる単語辞書をとっちめましょう。

まずは単語クラス辞書を見る

「単語クラス」というのは、単語をランダム生成するための起点となっている単語辞書を指します。とりあえずどこいつランダム単語をでっち上げるときに、最初に探すところだと思ってください。以下、HTMLを引用します。

<div class="単語">
<p class="色々"><q>用言</q>
<p class="複合名詞 景品 ポスター"><q>〜に〜する誰か</q>」のA3ポスタープレゼント
<p class="複合名詞 景品 目覚まし時計"><q>人名</q>目覚まし時計」
<p class="複合名詞 景品 麦わら帽子"><q>人名</q>麦わら帽子」
<p class="複合名詞 景品 安CDR">「安CD-R<q>一桁数字</q>枚組セット」
<p class="複合名詞 景品 人形">「等身大<q>人名</q>人形」
<p class="複合名詞 景品 1年ぶん"><q>人名</q>1年ぶん」
<p class="複合名詞 景品 1年ぶん"><q>〜に〜する誰か</q>1年ぶん」
<p class="複合名詞 X点で景品がX">20点で<q>景品.ポスター</q>
  ...
</div>

これはひどい。単語1要素はP要素で表現しています。華和梨辞書のkeyに相当するのがclass属性、valueに相当するのが値です。華和梨では1行で、一連の値に対して複数のkeyを,区切りで与えることが出来ましたが、乱歌において同様のことはclass内に空白区切りで複数のkeyを設置することで可能となります。さて、この辞書を、HTML5が読めるブラウザで直接開いてみてください。‥‥おや、このHTMLから想像できる出力を考えると、予想外に読みやすく表示されていませんか?ということで、このhtmlがリンクしているcssを読んでみましょう。

表現豊かになったcssセレクタ

q{
  color: orangered;
  font-weight: bold;
}

div.単語::before{
  content: "<<単語辞書>>";
}
div.単語{
  display: block;
  padding: 4px;
  margin: 0px 0px 3px 0px;
  border: 1px;
  border-style: dashed;
  border-color: #666666;
  background-color: #EFEFAF;
}

div.単語 p::before{
  display: inline;
  content: "[" attr(class) "] ";
  color: mediumblue;
}
div.単語 p{
  display: block;
  margin: 0px;
  padding: 0px;
  font-size: 80%;
  border-width: 1px 0px 0px 0px;
  border-style: dashed;
  border-color: #666666;
}

いやあ、最近のcssはいつの間にか怪しい記述になってます。わかんない!‥‥と投げる前に頑張って読み進めましょう。

q

これは簡単。「q」要素に対して、文字色をオレンジ・太字を設定しています。"〜に〜する誰か"とか、出てますね。

div.単語

先にこちらを。冒頭は「div」要素を対象とする宣言。続く「.単語」は、class指定です。"."で始まる、指定単語のclass(個々では「単語」)が含まれる要素をcssの適用対象としています。class="単語"を含む、div要素について、block要素として背景とか枠線を指定していますね。

div.単語::before

「::before」ってなんだ!はっ、そろそろ脱落者が出そうだな!ここは踏ん張りどころだ!‥‥えー、これは「疑似要素」と言います。似非要素でも良いかもしれませんが他言は無用。元々のHTMLに存在しない情報を、あるルールに従って勝手に付け加えるので、疑似要素なのです。ここでは、「div.単語」要素の「先頭(::before)」に、"<<単語辞書>>"という「content(文字列情報)」を付け加えますよ、と宣言しています。
先ほどのhtmlのウェブ出力と、元々のHTMLを比べてみてください。HTML側には"<<単語辞書>>"なんて単語はありませんよね?なのに表示の1行目に出力されています。
なお、IE8は::before疑似要素は対応していません。そのため、IEにおいてはこの要素は表示されないことになります。

div.単語 p

また先にこちら。なんですかこのスペース区切りは。div〜の後に、pがありますが、これは「最初の要素の中」に含まれる、「次の条件を満たす要素」にマッチします。この場合で言えば、class="単語"であるdiv要素の中にある、p要素、となります。この辞書記述ではp要素が辞書の1つのアイテムですので、それが分かるように編集しています。要素をボーダーラインの点線でくるんで見やすくしているわけです。

div.単語 p::before

これでやっと最後だ!さっきも出てきましたね、before。先のp要素の先頭に、content文字列やらを貼り付けています。色とかも指定してますが大事なのはcontentの中の「attr(class)」って部分。実はcontentでは単純な文字列のほかに便利な関数がいくつか用意されています。この関数は「要素"class"の中の値を表示せよ」という関数です。通常、要素タグの中の属性については直接HTMLで表示することは出来ませんが、このようにすることで、任意の属性値を表示可能となります。

cssセレクタを呼び出す魔法、「querySelectorAll」

さて、ここまでだと「表現力豊かになって便利だねーcss!」で終わる話ですが。実はこの高度なセレクタAPIは関数として公開されています。IE8、およびHTML5をサポートするブラウザが持つ「Selectors API」というものです。HTMLドキュメントや任意の要素に、このセレクタ文字列をそのまま引数としてquerySelectorAll関数を呼び出すと、用件に一致した要素がすべて列挙されます。
例えば"div.単語 p.景品"とすると、「景品」クラス指定がされている要素が列挙できます。and指定は?cssセレクタですから、例えば"div.単語 p.景品.ポスター"とすれば、上の例で言えば一番最初の要素だけが条件に適合するので、1つだけ選択した一覧が作成されるのです。

q要素でどこいつ発動!

どこいつ展開の発動はq要素で囲まれた文字列が見つかるたびに行っていますが、単語検索にはcssセレクタをそのまま使っています。jQueryでも同じことが出来るのですが、今回はネイティブAPIを使ってみたかったのでSelectors APIをそのまま使いました。なお、jQueryの新バージョンでは恐らくネイティブAPIを使うような実装に変わると思うので、そちらも期待ですね。
単語辞書の実装はこんな感じです。今のところ単語展開に関数をかませる実装は行っていませんが、JavaScriptなのでそのあたりはすんなり出来るんじゃないかなー?大事なのは「華和梨辞書の思想をそのまま持ち込めたこと」「ネイティブAPIはさすがに早い」「実装が楽だ」ということ。cssセレクタにはcss3になってかなり複雑な条件比較も持ち込めるようになっていますので、わりと融通が利く辞書が実現できてるんじゃないかな、と思ってます。楽な時代になったものです。
‥‥はっ、長くなった!ということで単語辞書編は終わり。あまり時間おかずに会話辞書の話をしたかったのですが、ちと仕事で出張とか入るので1〜2週間飛んじゃうかも?それでは、また。