2013年10月28日月曜日

jQueryで要素の置換 select要素(セレクトボックス)←→textタイプのinput要素(テキストボックス)

jQueryで要素の置換は、replaceAllやreplaceWithを使う。

replaceWithを使う例として、div要素の置換は、よく見かけるけど、select要素の置換はあまり見かけなかったので、メモっとく。


「radioタイプのinput要素(ラジオボタン)を変更した場合に、他のselect要素を選択できなくさせたい」って時に、disabledにするとグレーアウトされて、見た目の変更ができなくて困った。

で考えたのが、readonlyで行こうと。(submitされるけどね)
readonlyなら、フォントの色の変更とかができて自由だし。

でも、select要素には、readonlyがない。

ということで、select要素をtextタイプのinput要素(テキストボックス)に置換して、readonlyにすることで、落ち着いた。


jQueryならシンプルで簡単にできると思ったら、functionの実行時に、DOM操作が即時反映されないことが仇になり、結構苦戦。

素のJavaScriptでやってしまったら負けだと思って、追求したら、なんとかできた。

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Example</title>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    <script type="text/javascript">
    var example = {
        /**
         * idをキーにtextタイプのinput要素に置換したselect要素を保持する連想配列.
         */
        clonedSelects: {}
        ,
        /**
         * select要素をtextタイプのinput要素に置換する.
         * @param id select要素のid
         * @param value 固定のvalueを指定する(未指定の場合、現在選択中のvalueが設定される)
         */
        replaceToText:
            function (id, value) {
                // id内の.(ドット)はエスケープ
                var elem = $('#' + id.replace(/\./g, '\\.'));
                // select要素でない場合、処理終了
                if (elem[0].nodeName != 'SELECT') {
                    return;
                }
                
                example.clonedSelects[id] = elem.clone(true);
                
                // 固定のvalueを設定
                if (typeof value !== 'undefined') elem.val(value);
                
                // select要素のvalue値をhiddenで持つ
                var elemHidden = $('<input type="hidden"/>');
                // replaceWithを行うまではidが重複するため、別のidにする
                elemHidden.attr('id', id + 'Hidden');
                elemHidden.attr('name', elem.attr('name'));
                elemHidden.val(elem.val());
                elem.after(elemHidden);
                
                // select要素をtextタイプのinput要素に置換
                elem.replaceWith(function (elem, id) {
                    var elemText = $('<input type="text"/>').attr({readonly: 'readonly', tabindex: '-1'});
                    elemText.val(elem.children(':selected').text());
                    elemText.attr('style', elem.attr('style'));
                    
                    // textタイプのinput要素にするため、id,nameを変更
                    elemText.attr('id', id + 'Text');
                    elemText.attr('name', elem.attr('name') + 'Text');
                    return elemText;
                }(elem, id));
                
                // replaceWithを行った後、select要素のidをhiddenへ設定
                elemHidden.attr('id', id);
            }
        ,
        /**
         * textタイプのinput要素をselect要素に戻す.
         * "戻す"と記載しているのは、select要素を保持しているのが前提のため。
         * @param id 戻すselect要素のid
         * @param value 固定のvalueを指定する(未指定の場合、現在選択中のvalueが設定される)
         */
        replaceToSelect:
            function (id, value) {
                // select要素を保持する連想配列に存在しない場合、処理終了
                if (typeof example.clonedSelects[id] === 'undefined') {
                    return;
                }
                
                // id内の.(ドット)はエスケープ
                var elemHidden = $('#' + id.replace(/\./g, '\\.'));
                // replaceWithを行うとidが重複するため、事前に別のidにする
                elemHidden.attr('id', id + 'Hidden');
                
                // 固定のvalueを設定
                if (typeof value !== 'undefined') elemHidden.val(value);
                
                // id内の.(ドット)はエスケープ
                var elem = $('#' + id.replace(/\./g, '\\.') + 'Text');
                elem.replaceWith(function (id, value) {
                    var elemSelect = example.clonedSelects[id];
                    
                    // select要素に戻すため、idも戻す
                    elemSelect.attr('id', id);
                    
                    elemSelect.val(value);
                    
                    return elemSelect;
                }(id, elemHidden.val()));
                
                // select要素に戻したため、保持していたselect要素を削除
                delete example.clonedSelects[id];
                
                // select要素に戻すため、hiddenを削除
                elemHidden.remove();
            }
    }
    </script>
</head>
<body>

<input type="radio" name="radioexample" onclick="example.replaceToSelect('example');" checked="checked" />select要素へ置換
<input type="radio" name="radioexample" onclick="example.replaceToText('example')" />textタイプのinput要素へ置換

<br />

<select id="example" name="example">
    <option>サンプル1</option>
    <option>サンプル2</option>
    <option>サンプル3</option>
</select>

</body>
</html>

(注)select要素の置換なので、textタイプのinput要素を初期表示としたい場合、onloadでtextタイプのinput要素とすること。

(注)id内のドットのエスケープは、Strutsなどを使用していない限り、なくてもOK。


JavaScriptライブラリを比較するときに、役に立った本。
jQuery、Prototype、Dojo、YUI、script.aculo.usを比較してる。
今は、jQuery優勢かな。

0 件のコメント:

コメントを投稿