jQuery UI position で、表示方向に応じたクラスを付加する

##いつ使うものか
1. jQuery UI position で吹き出し型のツールチップを表示したい
2. `collision: 'flip'` での位置調整は便利だけど、上下・左右逆になったときの ▽ 部分の処理どうしよう
![ss.png](https://qiita-image-store.s3.amazonaws.com/0/73925/7ca6adce-84e9-f7f7-1f95-4cef7e408bad.png)← こういう感じの切り替え処理

3. 吹き出し型をやめれば気にする必要ないし、それでいいか...と妥協する

これの２対策。
Google の検索結果を見ると今一つ影の薄い jQuery UI position の、さらに場面限定ネタ。
ただ、やっている事はクラス付加なので他の使い道もあるかもしれない。

##完成品
▽ の切り替えは「現在どの方向に吹き出しが表示されているか」が分かれば対応可能なので、position 適用時に基準要素からの相対位置に応じてクラス `disp-top` `disp-btm` `disp-left` `disp-right` を付加し、それを使って CSS 側で調整を行う。

```HTML:HTML
<div class="box-tooltip">
<span class="cont-elm">sample text
<span class="cont-tooltip"><span class="inner">Tooltip text</span></span>
</span>
</div>
```

```CSS:CSS（標準が上辺中央表示、フリップ時に下辺表示に切り替わる場合）
.cont-tooltip {
position: fixed;
display: none;
z-index: 100;
}

.cont-tooltip .inner {
position: relative;
display: block;
padding: 0.5em;
background: #fee;
border: solid 2px #e33;
text-align: center;
}

/* 吹き出し ▽ 部分の基本指定 */
.cont-tooltip:after {
position: absolute;
content: "";
width: 10px;
height: 10px;
bottom: -5px;
left: 50%;
margin-left: -5px;
background: #fee;
border-bottom: solid 2px #e33;
border-right: solid 2px #e33;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}

/* 吹き出しを下側に出す際の ▽ の指定 */
.cont-tooltip.disp-btm:after {
top: -5px;
border: none;
border-top: solid 2px #e33;
border-left: solid 2px #e33;
}

```

```js:javascript（標準が上辺中央表示、フリップ時に下辺表示に切り替わる場合）
$(function() {
    $('.box-tooltip .cont-elm').hover(function(){
        $('.cont-tooltip',this).position({
            of: $(this),
            my: 'center bottom-10',
            at: 'center top',
            collision: 'flip',
            using: function(pos,info) {
                $(this).css(pos);
                var posV = (info.vertical == 'top'? 'btm' : 'top');
                var posH = (info.horizontal == 'left'? 'right' : 'left');
                $(this).removeClass('disp-top disp-btm disp-left disp-right');
                $(this).addClass('disp-' + posV + ' disp-' + posH);
            }
        }).show();
    },function(){
        $('.cont-tooltip',this).css({top:'0',left:'0'}).hide();
    });
});
```

position の flip 動作で表示する**辺**が切り替わる（▽ の切り替えが必要になる）のは「標準時とその反対側の辺」のペアだけで、上下左右には切り替わらない。
例えば、標準時「上辺の左側」表示で水平垂直両方に flip を設定した場合は「**上辺**の左側」「**上辺**の右側」「**下辺**の左側」「**下辺**の右側」の４種類の配置となり左辺と右辺は使用されない。上下左右に切り替わらないのは、おそらく flip の目的を考えると、ごく一部の特殊な状況を除いてこの動作で十分だからだと思う。
そのため、上記では汎用性を考え上下・左右の両方を出力しているが、切り替えで使用するのはどちらか一方だけの情報となる。
このあたりは、[jsdo.it でサンプルを作成](http://jsdo.it/tera1012/wQAe)したので、実際の動作を見たほうが分かりやすいかもしれない。

##解説：クラス付加は using で行う
表示位置に応じてクラスを付加するには `using` オプションを使う。
といっても、あらかじめパラメータとして相対位置情報が出力される仕様になっているので、それを取り出すだけで良い。

```js
...
            using: function(pos,info) {
                $(this).css(pos);
                var posV = (info.vertical == 'top'? 'btm' : 'top');
                var posH = (info.horizontal == 'left'? 'right' : 'left');
                $(this).removeClass('disp-top disp-btm disp-left disp-right');
                $(this).addClass('disp-' + posV + ' disp-' + posH);
            }
...
```

まず下準備として、 `using` を使用した場合、top と left の位置情報は `using` の関数内で改めて適用する必要があるため、`$(this).css(pos);` で適用する。
さらりと書いているが、この「関数内で改めて適用する必要がある」事に気づかずハマりまくった。 `using` は、あくまで ＋α のための追加オプションで、function の中身は通常処理後に実行されると思っていたのだ。改めて[リファレンス](http://js.studio-kingdom.com/jqueryui/method_overrides/position)を読むと当然きちんと説明されており、一通り読んでも理解できなかったマヌケさが恨めしい。

その後は、2つ目のパラメータに含まれる相対位置情報を取り出して addClass すれば良い。
なお、このパラメータにおける相対表現は「position で操作する要素から見て、基準となる要素はどこにあるか」となっているため、感覚的に分かりやすい「基準要素のどちら側に表示しているか」になるよう反転させている。
また、相対位置の値は `center` をとる場合もあるが、flip 動作には関係が無いため「上か否か」「左か否か」のみを判定し、上/下・左/右だけを出力している。（ `center` になるのは最初に指定した時のみで、またその場合は常に `center` なので判別する必要が無い。今回の例で言えば、左右方向は常に `disp-left` クラスが付加される）

##解説：using 内の this について
using 内では position の操作対象が新たに this と定義されるため CSS で調整する際のクラス位置に注意。
今回の例で `$(this).addClass` によってクラスが付加されるのは、大元の this（＝ hover の対象である `$('.box-tooltip .cont-elm')` ）ではなく、position の操作対象 `$('.cont-tooltip',this)` となる。
また、上記の理由により関数化して呼び出す場合も、using 内の this はそのままで良い。

```js:javascript（関数化して呼び出す例）
$(function() {
    $('.box-tooltip .cont-elm').hover(function(){
        var event = $(this)
        posSet(event);
    },function(){
        $('.cont-tooltip',this).css({top:'0',left:'0'}).hide();
    });

    function posSet(event) {
        $('.cont-tooltip',event).position({
            of: $(event),
            my: 'center bottom-10',
            at: 'center top',
            collision: 'flip',
            using: function(pos,info) {
                $(this).css(pos);
                var posV = (info.vertical == 'top'? 'btm' : 'top');
                var posH = (info.horizontal == 'left'? 'right' : 'left');
                $(this).removeClass('disp-top disp-btm disp-left disp-right');
                $(this).addClass('disp-' + posV + ' disp-' + posH);
            }
        }).show();
    }
});
```