Katashin .info

box-sizing: border-box; を指定している時の jQuery UI Resizable の調整方法

CSS の幅や高さ指定を直感的なものにしてくれる border-box というものがありますが、これを使うと jQuery UI の Resizable の挙動がおかしくなります。この問題は短くて単純なコードで解決することができます。

通常、CSS の width や height は HTML 要素のコンテンツのサイズを指定します。つまり、border や padding の値は width や height には含まれません。例えば、width が 100px で、左右の padding が 20 px の HTML 要素を表示してみると、見た目上の幅は 140px となるわけです。これは CSS を書いてると結構気持ち悪く感じることがあると思います(他の要素との組み合わせを考える時にめんどくさかったり)。

上記の問題(?)はCSS で、box-sizing: border-box; を指定することにより解決することができます。つまり、width や height の値を padding や border も含んだものとして指定できるようになります。先の例の width が 100px で、左右の padding が 20px の HTML 要素を考えると、見た目の幅が 100px となり、要素内のコンテンツの幅が 60px になります。

しかし、box-sizing を border-box にすると、jQuery UI の Resizable の挙動が気持ち悪くなります。具体的には、HTML 要素のサイズを変更するためにドラッグを開始すると、padding や border の値だけ大きさがずれます。このせいで、ドラッグ中のマウスポインタの座標と、HTML 要素の端が一致しないのですごく気持ち悪いです。

Resizable の border-box 問題に関しては、jQuery 側ではまだ対応されていないみたいなので、自分で対応してみました。以下がそのコードです。jQuery UI のコードを読み解くのは嫌だったので、普通な感じの解法です。$el が border-box と resizable の両方を適用している HTML 要素の jQuery オブジェクトです。

$el.on('resize', function (ev, ui) {
  // box-sizing: border-box; に対応
  ui.element.height(ui.size.height)
  ui.element.width(ui.size.width)
})

HTML 要素がリサイズされた時に発生するイベント "resize" に対してイベントハンドラを割り当て、この中で width や height の値を修正しています。"resize" イベントのイベントハンドラは第2引数に、ui オブジェクトが与えられます。ui.element はリサイズ対象の HTML 要素の jQuery オブジェクトで、ui.size はリサイズ後の HTML 要素のサイズを示しています。ui.size のそれぞれの値を ui.element の width、height 関数に与えるだけで border-box に対応することができます。これは、ui.size の値が padding や border の値を考慮していない値になっており、さらに、jQuery の width、height 関数は同様に padding や border の値を含まないのサイズを指定するようになっているためです(border-box っぽく指定したい時は css 関数で指定できます)。

border-box と Resizable を併用することで発生するサイズの問題は "resize" イベントのイベントハンドラで HTML 要素の正しいサイズを設定してやると解決できました。偶然だと思いますが、結構短いコードで解決できたのが面白いです。初めに真面目に padding と border を取得していたのは何だったのかと。