jQueryでダイアログっぽいものを作ってみた

# 動機

以前、JavaScriptを使って、HTMLベースのツールを作った。
ツールでは、コンテンツの表示領域をなるべく広くしたかったので、ボタンとかフォームとかの配置に困り、結局、半透明のパネル（モードレスダイアログ）に配置してコンテンツ上にかぶせることにした。

HTML5+JavaScriptでモードレスダイアログを作る方法を探したところ、`dialog`タグが見つかったがChromeしか対応していないらしい。
JavaScriptのライブラリを探したのだが意外と見つからないので、自作することにした。

# 設計

`Panel`クラスを作って、パネルの作成・表示を管理する。

パネルのDOMの構造を一定に保ちたいので、HTMLを直書きせずにDOMを自動生成することにする。
また、パネルの表示・非表示ボタンも自動生成し、ボタンを押したときのアクションも同時に登録する。

`Panel`のコンストラクタでDOMを生成してそのまま参照を保持して、という設計もあるが、オブジェクトとDOMが密結合になってしまう。
そこで、コンストラクタではDOMの'id'をプロパティに入れるにとどめ、別関数でDOMを生成することにした。
DOMを操作するときは`id`と`class`を使うことにする。


# 実装

要点だけ。

```javascript:
var Panel = (function() {
  var PANEL_CLASS = 'modeless-panel';
  var PANEL_Z = 1000;

  var Panel = function( title_ )
  {
    var self = this;
    self.title = title_;
    self.elmId = {
      'show': 'panel-show-' + title_,
      'panel': 'panel-frame-' + title_,
      'titlebar': 'panel-titlebar-' + title_,
      'body': 'panel-body-' + title_
    };
  };

  Panel.prototype.activate = function( active_ = true )
  {
    var self = this;
    $( '.' + PANEL_CLASS ).css( 'z-index', ( PANEL_Z - 1 ) );
    if ( active_ ) $( '#' + self.elmId.panel ).css( 'z-index', PANEL_Z );
  };

  Panel.prototype.show = function()
  {
    var self = this;
    $( '#' + self.elmId.show ).hide();
    $( '#' + self.elmId.panel ).show();
    self.activate();
  };

  Panel.prototype.hide = function()
  {
    var self = this;
    $( '#' + self.elmId.show ).show();
    $( '#' + self.elmId.panel ).hide();
  };

  Panel.create = function( title_ )
  {
    var panel = new Panel( title_ );

    // initialize show buttons' area, if not exist.
    var elmShowButtons = $( '#panel-show-buttons' );
    if ( elmShowButtons.length == 0 ) {
      elmShowButtons = $( '<div></div>', {
        'id': 'panel-show-buttons',
      } ).css( {
        'z-index': ( PANEL_Z - 2 ),
        'position': 'fixed',
        'top': '0px',
        'right': '0px'
      } ).appendTo( 'body' );
    }

    // create show button.
    var elmShow = $( '<button></button>', {
      'id': panel.elmId.show,
      'text': title_,
      'click': function( e_ ) { panel.show(); }
    } ).css( {
      'display': 'none'
    } ).appendTo( elmShowButtons );

    // create titlebar with hide button.
    var elmHide = $( '<button></button>', {
      'text': 'x',
      'click': function( e_ ) { panel.hide(); }
    } );
    var elmTitlebar = $( '<div></div>', {
      'id': panel.elmId.titlebar,
      'text': title_
    } ).prepend( elmHide );
    // create empty body.
    var elmBody = $( '<div></div>', {
      'id': panel.elmId.body
    } );
    // create panel with titlebar and body.
    var elmPanel = $( '<div></div>', {
      'class': PANEL_CLASS,
      'id': panel.elmId.panel,
      'click': function( e_ ) { panel.activate(); }
    } ).css( {
      'z-index': 1000,
      'position': 'fixed',
      'top': $( '.' + PANEL_CLASS ).length * 20 + 'px',
      'left': $( '.' + PANEL_CLASS ).length * 20 + 'px'
    } ).append( elmTitlebar, elmBody ).appendTo( 'body' );

    return panel;
  };

  return Panel;
})();
```

# 使用例

```html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>panels</title>
    <!-- jQuery -->
    <script src="js/jquery-3.2.1.js"></script>
    <!-- panel -->
    <script src="js/panel.js"></script>
    <script>
      $( function() {
        var foo = Panel.create( 'foo' );
        $( '#' + foo.elmId.body ).text( 'I am foo.' );
        var bar = Panel.create( 'bar' );
        $( '#' + bar.elmId.body ).text( 'I am bar.' );
        bar.hide();
      } );
    </script>
  </head>
  <body>
  </body>
</html>
```

# 拡張案

- __デザイン__：DOMに`id`と`class`を付与してcssを書けば、色とか大きさとかを変更できる。
- __パネルのドラッグ__：イベント`mousedown`, `mouseup`,`mousemove`をハンドルしてマウスの移動量をパネルの位置に反映すれば、ドラッグ移動できるはず。
    - __パネルの中身__：HTMLで描く場合は、パネル作成後に`$('#bodyid').detach().appendTo('#'+panelobj.elmId.body)`とすれば良い。

# 終わりに

React.jsとかAngular2とか使えばよかったのかもしれない。
しかし、作ったツールが、Webサーバ不要でブラウザだけで動作するものだったので、大仰な仕組みは不要と思いい自作してしまった。
最終的に色々と機能を追加してしまったので、手間を考えるとそれらのライブラリに頼った方が良かったかもしれない。