select-optionタグのメニューリストの各<option>タグの項目を、表示する瞬間にAjaxで動的に書き換える必要が仕事で発生した。
当初は"onMouseDown"イベントで書き換えようとしていたが、IEの場合にリストが上手く展開されない現象に悩まされた。
一時、<ul><li>タグでCSSを使ったドロップダウンリストで実装してみたが、今度はFlashが真下に来た場合のz-indexが効かない。
Flashの方でwmodeをtransparentにすることで回避できるが、代わりに、Flash中でテキストボックスがある時、IMEによる日本語入力が正常に表示出来ないバグがある事が判明し、wmode指定による手法は使えない事が分かった。
最終的に"onMouseOver"イベントで書き換える様にした。つまり、ユーザーがselectメニューの上にマウスを置いた時点で、<option>を書き換える。
書き換える際はAjaxによる通信で最新のリスト項目を取得する為、パフォーマンスに若干の不安があるが、アクセスがかなり限定される箇所だった事・取得元データ(MySQL)が元から非常に更新頻度が激しくフルスキャン前提のテーブル設計だった事などより、パフォーマンス的には意識する必要がないか、意識してもしょうがない状態だったため、そのままonMouseOverを採用する事にした。
作業現場では「あーでもない、こーでもない」と10個位試作品を作っていたが、今日、記憶を頼りにある程度家のPC上でゴールに至る道のりを再現したのでメモ代わりに書いておく。
HTML/JavaScriptなどは一通り次のzipファイルに収めてあるので、実際に動かしてみたい方はダウンロードして下さい。
dynamic_dropdown_menulists.zip
最初に作ってみたのが"onMouseDown"イベントで書き換えるパターン。<select>要素に対するイベントは幾つかあったのだが、とりあえず alert() で試してみて上手く取れたのが onMouseDown だった。
ちなみにselectメニューの「▼」マークをクリックして<option>の項目リストがドロップダウンするアクション自体は、JavaScriptからは動かせない。
dynamic_dropdown_menulists.zip 中の "dynamic_ddlist_bad.html" を参照。
function show_selection(ddl_id) { var r = $(ddl_id); var label = r.options[r.selectedIndex].text; alert(label); } function update_ddl(ddl_id, change_label) { var s = $(ddl_id); for (var i = 0; i < s.options.length; i++) { var label = s.options[i].text; if (change_label) { label = label + " - " + i; } s.options[i].text = label; } }
二つの<select>メニューがあり、左側が"onMouseDown"でも<option>のラベルが変化しない場合。右側が"onMouseDown"で<option>のラベルが変化する場合。
<tr> <td> <select id="ddl1" size="1" onMouseDown="update_ddl('ddl1', false)" onChange="show_selection('ddl1')" > <option value="100">foo</option> <option value="200">bar</option> <option value="300">baz</option> </select> </td> <td> <select id="ddl2" size="1" onMouseDown="update_ddl('ddl2', true)" onChange="show_selection('ddl2')" > <option value="100">foo</option> <option value="200">bar</option> <option value="300">baz</option> </select> </td> </tr>
Firefox/Chromeなどの場合は左右の<select>で動作が変わらず、"onMouseDown"で<option>のラベルが変化する場合も、特に表示や挙動が変わる事はない。
一方IEの場合、<option>のラベルが変わると<option>項目のドロップダウン挙動が不安定になる。ドロップダウンしたりしなかったりする。
あれこれ弄ってみたのだが結局上手く動かず、一時は断念し、<ul><li>タグをCSSで制御する方式を試みた。
CSSの制御で手こずったが、どうにか動くところまでは持ってゆけた。
→ dynamic_dropdown_menulists.zip 中の "css_dynaddl.html", "dynadll.js" を参照。
ところが、今回の要件ではドロップダウンした項目リストがFlashと重なってしまう部分があった。そのままだと、z-indexをどんなに大きな数値にしても、Flashの下に隠れてしまう。
→ dynamic_dropdown_menulists.zip 中の "css_dynaddl_conflicts_flash.html" を参照。
(ちなみに使っているFlashは、gihyo.jpでのActionScript入門連載記事に出てきたFlashです。ありがとうございました。)
調べてみたところ、"wmode"というHTML側で指定するFlashのパラメータを "transparent" にすれば回避できる事が分かった。
→ dynamic_dropdown_menulists.zip 中の "css_dynaddl_wmode_transparent.html" を参照。
ところが"wmode:transparent"は、IMEオンでFlash内のテキストボックスに文字を入力する時、入力文字の表示がおかしくなるバグがあるとのこと。
今回の仕事で使うFlashも、まさしくこのケースに該当する。よって、"wmode"パラメータはあえなく使えなくなってしまった。
もはや駄目元でやってみたのだが、これでようやくIEでもちゃんとドロップダウンの挙動が安定した。
→ dynamic_dropdown_menulists.zip 中の "dynamic_ddlist_good.html" を参照。
<select id="ddl2" size="1" onMouseOver="update_ddl('ddl2', true)" onChange="show_selection('ddl2')" > <option value="100">foo</option> <option value="200">bar</option> <option value="300">baz</option> </select>
<select>タグを使ったメニューのAjaxによる更新だが、例えば「大カテゴリを選択→小カテゴリをそれに連動させる」というように、二つの<select>を連携させる例は良く見つかる。
しかし今回のように一つの<select>の内容をドロップダウン時に更新するというサンプルはさくっと検索したレベルでは見つからなかった。
また<select>タグによるメニューの呼称も、「プルダウンメニュー」「ドロップダウンメニュー」「selectメニュー」などあり得る為、検索キーワードの設定が難しかった。印象だが、英語圏では"drop down list"という呼称が多かったような気がする。
jQuery UIが使えれば、予めCSSが調整済のパーツを使えるので手間も大分省けたのかも知れないが、今回の仕事は不慣れなprototype.jsだったためかなり手こずった。
コメント