IE でセレクトボックスの上に要素を描画出来ない件

よそのブログとかでも色々言及されてますが、私も言及してみる。

Internet Explorer なるブラウザは、セレクトボックス*1の描画に関して、Windows 標準のコントロールを用いています。

どーゆーことかってーと、「ファイルを開く」ダイアログのドライブ選択のプルダウンやら、エクスプローラの「アドレスバー」のプルダウンやらと同じモノが使われてるってことです。

まぁ、別にそれ自体は構わないとは思いますが、そのせいで一つ困った問題が起きるわけです。

例えば、「JavaScript でポップアップメニュー的なものを実装したい!」とか思ったときに、ポップアップさせる要素の大きさがセレクトボックスに被さるような大きさだった場合、ポップアップさせる要素の z-index とかを大きくしてもセレクトボックスの方が上に表示されてしまうのです。

それの解決方法*2をエントリしてみる。


まずは普通にポップアップさせるためのコードを書いてみる。

手続きとしてはこんな感じ。

  • あらかじめポップアップさせる要素を display: none で準備しておく。
  • ある要素をクリックした際にポップアップさせる要素を cloneNode() してマウスカーソルの右下に描画する
<div id="popup" style="display: none;">
  ...
</div>
<p>
  <a href="javascript: void(0);" id="popup-trigger">ポップアップ表示</a>
</p>
var Monry_Popup = Class.create(
  {

    open: function(event) {
      var popup = $('popup').cloneNode(true);
      popup.setStyle(
        {
          position  : 'absolute',
          left      : event.pointerX() + 'px',
          top       : event.pointerY() + 'px'
        }
      );
      popup.show();
    },

    initialize: function() {
      // Do nothing...
    }

  }
);

var popup = new Monry_Popup;
Event.observe('popup-trigger', 'click', popup.open);

これで多分表示されます。

まぁ、非表示にする処理を書いてないので、そこは脳内補完でよろw

ここまでは問題無いわけですが、ここからが問題なわけよ。

もし、ポップアップが表示される右下の領域にセレクトボックスがあった場合、ポップアップされる要素の上にセレクトボックスが堂々と居座ってくれるわけですよ。

それの解決には、iframe を使えば良いわけです。

Internet Explorer のセレクトボックス要素は、フレーム要素を除くほかの要素よりも強いようなので、フレーム要素の子要素である iframe 要素をつかってあげれば解決するわけです。

サンプルコードは以下。

<div id="container">
  <div id="popup" style="display: none;">
    ...
  </div>
  <p>
    <a href="javascript: void(0);" id="popup-trigger">ポップアップ表示</a>
  </p>
  <p>
    <select name="hoge">
      <option value="foo">ふ〜〜〜〜〜</option>
      <option value="bar">ば〜〜〜〜〜</option>
    </select>
  </p>
</div>
var Monry_Popup = Class.create(
  {

    open: function(event) {
      var iframe = new Element(
        'iframe',
        {
          id: '_popup_iframe',
          src: false,
          frameborder: '0'
        }
      );
      var base = new Element(
        'div',
        {
          id: '_popup_base'
        }
      );
      var popup = $('popup').cloneNode(true);
      base.insert({bottom: popup});
      iframe.setStyle(
        {
          display   : 'none',
          width     : popup.getWidth() + 'px',
          height    : popup.getHeight() + 'px',
          position  : 'absolute',
          left      : event.pointerX() + 'px',
          top       : event.pointerY() + 'px',
          zIndex    : '10',
          padding   : '0px',
          margin    : '0px',
          border    : 'none'
        }
      );
      base.setStyle(
        {
          display   : 'none',
          position  : 'absolute',
          left      : event.pointerX() + 'px',
          top       : event.pointerY() + 'px',
          zIndex    : '20'
        }
      );
      $('container').insert({bottom: iframe});
      $('container').insert({bottom: base});
      iframe.show();
      base.show();
    },

    initialize: function() {
      // Do nothing...
    }

  }
);

var popup = new Monry_Popup;
Event.observe('popup-trigger', 'click', popup.open);

ポイントは

  • 描画用の箱となる div 要素と、セレクトボックスを隠匿するための iframe 要素を JavaScript で構築する
  • 箱用の div 要素の子要素として、ポップアップさせる要素を突っ込む
  • iframe が大きすぎても小さすぎてもいけないので、ポップアップさせる要素のサイズに合わせる
  • 「iframe の z-index < div の z-index」となるように値を調整する
  • 最後に iframe -> div の順で表示させる

と、いった感じ。

もし、これで巧くいかない場合は、$('container').insert(...) の後にも setStyle を書くと巧くいくかもです。

この辺の順序は調べる余裕無かったんで、try & error でお願いしますw

*1:リストボックスやプルダウンなどとも呼ばれる

*2:Prototype.js を使う前提で話を進めます。