jquery.balloon.js で clone() して UI を作るときに要素を特定する方法

## はじめに

jquery.balloon.js にて入力補助の UI を作成しました。このような場合、隠し表示にしてある div などを clone() してバルーン内部のコンテンツを作ると思いますが（と思っているのですが）このとき ID 属性がダブったりして値の取得・設定などしたい場合、思ったようにいかないことがあります。

## クローン時に addClass() する

末尾に全体のサンプルがあります。クローンを作るときにメソッドチェーンをもう一つつなげて `$('#balloon_contents').clone().addClass('cloned_contents')` としてクラスを追加します。

## バルーンの中かどうかの判定

追加したクラスを `if ($(this).closest('.cloned_contents').length > 0)` のように使い、バルーンの中にあるコントロールかどうかを判定します。

## イベントハンドラの重複設定防止

バルーンが初期化されるたびにイベントハンドラが設定されるので、重複を防止します。サンプルで `$('.cb_in_balloon').off('click');` の部分です。

## コンテンツ内部の ID 属性を書き換える

`checkbox` に対する `label for='...'` など、ID に依存するもののために、ID 属性を書き換えます。バルーンの中のコンテンツだけを書き換えます。

チェックボックスとラベルについてはこれをしないと、バルーン上のラベルをクリックしても元コンテンツ側のチェックボックスが反応してしまいます。

チェックボックスとラベルについてはサンプルコードに記しました。その他の input については必要に応じて対応してください。

## バルーン内部のコントロールにイベントハンドラを設定する

セレクタを指定してイベントハンドラを設定するのではなく、セレクタを指定してイテレート（ `each()` ）して、バルーンの中のコントロールかどうかを判定してから個別にイベントハンドラを設定します。

## 全体サンプル

【お詫び】jQuery 本体と jquery.balloon.js 本体は別途ご用意ください。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="">
    <script src="jquery.min.js"></script>
    <script src="jquery.balloon.min.js"></script>
    <script type="text/javascript">
    $(/*document).ready(*/function () {
        var firstShow = true;
        var balloonObj = {
              html: true
            , contents: $('#balloon_contents').clone().addClass('cloned_contents') // コンテンツをクローンするときにクラスを追加する。
            , position: "right"
            , offsetX: -5
            , showComplete: function () {
                
                // バルーンが初期化されるたびにここに来るので、クリックイベントの
                // ハンドラが重複しないように一旦解除する。
                $('.cb_in_balloon').off('click');
                
                // ラベルのためにクローンした方（つまりバルーン内部のコンテンツ）の ID を変更する。
                var seq = 0;
                $('.cb_in_balloon').each(function () {
                    if ($(this).closest('.cloned_contents').length > 0) {
                        var origin = $(this).attr('id');
                        var newId = origin + seq;
                        $(this).attr('id', newId);
                        $('label[for=' + origin + ']').each(function () {
                            if ($(this).closest('.cloned_contents').length > 0) {
                                $(this).attr('for', newId);
                            }
                        });
                        seq++;
                    }
                });
                
                // バルーン内部のコンテンツにクリックイベントを設定する。
                $('.cb_in_balloon').each(function () {
                    if ($(this).closest('.cloned_contents').length > 0) {
                        $(this).click(function () {
                            alert('Clicked in Balloon.');
                        });
                    }
                });
            }
        };
        var selector = '#base';
        $(selector).focus(function () {
            $(this).showBalloon(balloonObj);
        });
        $(selector).click(function () {
            $(this).showBalloon(balloonObj);
        });
        $(selector).blur(function () {
            $(selector).hideBalloon(balloonObj);
        });
    });
    </script>
</head>
<body>
    <div class='balloon_outer_div'>
        <!-- 通常、この div は非表示にすると思う -->
        <div id='balloon_contents'>
            <input type='checkbox' class='cb_in_balloon' id='cb1' />
            <label for='cb1'>チェックボックス1</lable>
            <input type='checkbox' class='cb_in_balloon' id='cb2' />
            <label for='cb2'>チェックボックス2</lable>
        </div>
    </div>
    
    <input type='text' id='base' />
</body>
</html>
```

## おわりに

なんかもっと良い方法ないかな。「jQuery を使わない」というのはおいといて。