LoginSignup
1
0

More than 5 years have passed since last update.

Mithrilで選択されるたびにもとに戻る<select>

Posted at

選択されると、要素を作るUIが作りたいとします。本来は、その要素数分、ボタンを並べれば良いのですが、スペースの関係でボタンを隠しておきたい場合があります。CSSをがんばるのもあれなので、コンボボックスにしておいて、それが選択されたらボタンが押されたようにふるまうようにしてみようと思います。

例えば、シムシティで、序盤のチュートリアルからよく使う「住宅地区」「商業地区」「工業地区」「警察」「消防」「発電所」「道路」あたりは専用のボタンを置いておくとして、ちょっと大きくならないと使わない娯楽施設系も全部表示されていると邪魔ですよね。そういうあまり使わない建物を隠しておく感じのUIです。

だめな例

<option>タグのselected属性が選択されている<option>を表すので、先頭要素だけ常にtrueならいけるでしょうか?シムシティを例にあげといてなんですが、スーパーファミコン時代のしかやったことがなくて詳しくないので名前の一覧はここを参照しました。

const labels = [
    '(娯楽施設建造)',
    'ホテル', '円形劇場', 'エキスポセンター', 'グローブ座', 'スタジアム',
    'シドニー・オペラハウス', '観覧車', '相撲会館', 'オスロ・オペラハウス'];    

return m('select', {
    onchange: m.withAttr('selectedIndex', ctrl.selected),
}, labels.map((label, index) => {
    return m("option", {selected: index === 0}, label);
}));

コレではダメです。というのも、表示後にユーザが操作すると、実際のDOMの選択状態が変更されるだけで、仮想DOM上はMithrilが「先頭要素が選択されている」と思い続けている状態になります。そのため、再更新が走ったとしても、「変更がない」と判断されてしまいます。

OKな例

次のような関数を作り、実DOM上でインデックスをリセットすればOKです。

function resetIndex(element, isInitialized) {
    if (isInitialized) {
        window.requestAnimationFrame(() => {
            element.selectedIndex = 0;
        });
    }
}
const labels = [
    '(娯楽施設建造)',
    'ホテル', '円形劇場', 'エキスポセンター', 'グローブ座', 'スタジアム',
    'シドニー・オペラハウス', '観覧車', '相撲会館', 'オスロ・オペラハウス'];    

return m('select', {
    onchange: m.withAttr('selectedIndex', ctrl.selected),
    config: resetIndex
}, labels.map((label, index) => {
    return m("option", label);
}));

これはバグではなくて規定の動作

さて、これはバグでしょうか?そうではありません。Mithrilはある程度の実DOM上だけの状態は仮想DOMと切り離してそのまま放置しています。そうでなければ、テキスト入力フォームが毎回再生成されてしまいます。IMEがONの状態で入力中のテキストなどはJavaScriptで取得したり復元したりはできないため、仮想DOMの再生成が厳しく行われると困ったことになってしまいます。そのため、これはこれでいいのです。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0