Form.Elementオブジェクト

【抜粋】一部省略
Form.Element = {
  serialize: function(element) {
(省略)
  },

  getValue: function(element) {
(省略)
  }
}

inputタグ等の入力要素を扱うオブジェクト。Formオブジェクトと違い、form経由でなくても要素を扱えます。

serializeメソッド

【抜粋】
  serialize: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter) {
      var key = encodeURIComponent(parameter[0]);
      if (key.length == 0) return;

      if (parameter[1].constructor != Array)
        parameter[1] = [parameter[1]];

      return parameter[1].map(function(value) {
        return key + '=' + encodeURIComponent(value);
      }).join('&');
    }
  },

引数の入力要素(ID指定可)から、"(name属性)=(value属性)"の文字列を作成して返却するメソッド。selectタグでmultiple指定がある場合は、"(name属性)=(value属性1)&(name属性)=(value属性2)"のように「&」で繋げて返却します。複数の同名radioやcheckboxには対応していないので注意してください。

Form.Element.Serializersは、入力要素のタグと同じ名前のメソッドを備えています。これらのメソッドは、対応する入力要素を元に [(name属性), (value属性)] という配列を作成して返却します。selectタグでmultiple指定がある場合、返却する配列は [(name属性), [(value属性1), (value属性2), ・・・ ] ] となります。

これを元に、返却する文字列を作成しています。name、valueともにすべてURIエンコードされます。

【例】
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function test(element){
  var str;
  if(element.length != null && element.type == null){
    str = $A(element).collect(function(elem){
      return Form.Element.serialize(elem);
    }).compact().join("&");
  }else{
    str = Form.Element.serialize(element);
  }
  Element.update('test', str);
}
//-->
</script>
</head>
<body>
<form onsubmit="return false;">
<input type="text" name="TEXT" value="テキストボックス"/><br/>
<button onclick="test(this.form.TEXT);">TEST</button><br/>
<br/>
<input type="radio" name="RADIO" value="RADIO1" checked/>
<input type="radio" name="RADIO" value="RADIO2"/><br/>
<button onclick="test(this.form.RADIO);">TEST</button><br/>
<br/>
<select size="3" name="SELECTSIZE" multiple>
  <option value="OPTION1" selected>オプション1</option>
  <option value="OPTION2">オプション2</option>
  <option value="OPTION3" selected>オプション3</option>
  <option value="OPTION4">オプション4</option>
</select><br/>
<button onclick="test(this.form.SELECTSIZE);">TEST</button><br/>
<br/>
<button onclick="Element.update('test', decodeURIComponent($('test').innerHTML));">
DECODE</button>
</form>
<div id="test"></div>
</body>
</html>

各「TEST」ボタンを押すと、直前の要素から文字列を作成して表示します。DECODEボタンで表示文字列をURIデコードします。test関数ではRADIOボタン用に処理を分岐しています。(判定ロジックはてきとーです^^;*1 )

Arrayクラスの追加メソッドを使っています。簡単に説明すると、要素の集合elementを配列化して、それぞれのserializeの結果を配列に格納し(collectメソッド)、そこからnulll(undefined)を除外(compactメソッド)した上で、既存joinメソッドで文字列化しています。

getValueメソッド

【抜粋】
  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter)
      return parameter[1];
  }

引数の入力要素(ID指定可)から、value属性の値を返却するメソッド。selectタグでmultiple指定がある場合は、値を格納した配列を返却します。こちらも複数の同名radioやcheckboxには対応していないので注意してください。

【例】前述serializeメソッドの例のtest関数を以下に変更してください^^;
function test(element){
  var value;
  if(element.length != null && element.type == null){
    value = $A(element).collect(function(elem){
      return Form.Element.getValue(elem);
    }).compact();
  }else{
    value = Form.Element.getValue(element);
  }
  Element.update('test', Object.inspect(value));
}

やっぱり、複数の同名radioやcheckboxにも対応してくれないと、使いにくい気がするのですが。。。

2006/09/13追記。改修案を作ろうと思いましたが、Form.Elementクラスのメソッドはprototype.js内の他の部分で使用されているため、メソッド上書きはちょっとやりにくいです。なので別のメソッドを追加することを検討してみました。

【改修案】追加メソッド
  serialize2 : function(element){
    var retStr;
    element = $(element);
    if(element.length != null && element.type == null){
      retStr = $A(element).collect(function(elem){
        return this.serialize(elem);
      }.bind(this)).compact().join("&");
    }else{
      var tags;
      if(element.form){
        tags = element.form.elements[element.name];
      }else{
        tags = document.getElementsByName(element.tagName);
      }
      if(tags.length != null && tags.length > 1 && tags.type == null){
        retStr = tags.collect(function(tag){
          return this.serialize(tag);
        }.bind(this)).compact().join('&');
      }else{
        retStr = this.serialize(element);
      }
    }
    return retStr;
  },
    
  getValue2 : function(element){
    var retVal;
    element = $(element);
    if(element.length != null && element.type == null){
      retVal = $A(element).collect(function(elem){
        return this.getValue(elem);
      }.bind(this)).compact();
    }else{
      var tags;
      if(element.form){
        tags = element.form.elements[element.name];
      }else{
        tags = document.getElementsByName(element.tagName);
      }
      if(tags.length != null && tags.length > 1 && tags.type == null){
        retVal = tags.collect(function(tag){
          return this.getValue(tag);
        }.bind(this)).compact();
      }else{
        retVal = this.getValue(element);
      }
    }
    return retVal;
  }

引数elementはID指定、要素指定、要素集合指定に対応しています。ID、要素指定の場合は所属フォーム内(所属フォームがない場合はドキュメント全体)に同じ名前の要素があれば処理対象に加えます。要素集合指定時や同一名要素がある場合、返却結果はslelectタグのmultiple指定時と同じようになります(serialize2は「&」で連結された文字列、getValue2は配列。getValue2ではラジオボタンは通常、単一要素配列となります)。

formとnameで指定したい場合はdocument.(フォーム名).(要素名)*2等で指定してください。(実際は、form、name指定で値を取得できるほうが使い勝手がいいような気がしますが。そうすると完全に別物になってしまうのでパス^^;)

もしprototype.jsに追加する場合は、メソッド間の「,」付け忘れに注意してください。下の例のように別に定義するのもいいと思います。

【参考】改修案を用いた場合の例
<html>
<head>
<title></title>
<script language="javascript" src="prototype.js" charset="utf-8"></script>
<script>
<!--
function test(element){
  Element.update('test1', Form.Element.serialize2(element));
  Element.update('test2', Object.inspect(Form.Element.getValue2(element)));
}
//メソッドの追加
Form.Element.serialize2 = function(element){
  var retStr;
  element = $(element);
  if(element.length != null && element.type == null){
    retStr = $A(element).collect(function(elem){
      return this.serialize(elem);
    }.bind(this)).compact().join("&");
  }else{
    var tags;
    if(element.form){
      tags = element.form.elements[element.name];
    }else{
      tags = document.getElementsByName(element.name);
    }
    if(tags.length != null && tags.length > 1 && tags.type == null){
      retStr = $A(tags).collect(function(tag){
        return this.serialize(tag);
      }.bind(this)).compact().join('&');
    }else{
      retStr = this.serialize(element);
    }
  }
  return retStr;
}
  
Form.Element.getValue2 = function(element){
  var retVal;
  element = $(element);
  if(element.length != null && element.type == null){
    retVal = $A(element).collect(function(elem){
      return this.getValue(elem);
    }.bind(this)).compact();
  }else{
    var tags;
    if(element.form){
      tags = element.form.elements[element.name];
    }else{
      tags = document.getElementsByName(element.name);
    }
    if(tags.length != null && tags.length > 1 && tags.type == null){
      retVal = $A(tags).collect(function(tag){
        return this.getValue(tag);
      }.bind(this)).compact();
    }else{
      retVal = this.getValue(element);
    }
  }
  return retVal;
}
//-->
</script>
</head>
<body>
<form onsubmit="return false;" name="FORMNAME">
<input type="text" name="TEXT" id="textID" value="テキストボックス"/><br/>
<button onclick="test('textID');">TEST</button><br/>
<br/>
<input type="radio" name="RADIO" value="RADIO1" checked id="radioID"/>
<input type="radio" name="RADIO" value="RADIO2"/><br/>
<button onclick="test(document.FORMNAME.RADIO);">TEST</button>
<button onclick="test($('radioID'));">TEST</button><br/>
<br/>
<select size="3" name="SELECTSIZE" multiple>
  <option value="OPTION1" selected>オプション1</option>
  <option value="OPTION2">オプション2</option>
  <option value="OPTION3" selected>オプション3</option>
  <option value="OPTION4">オプション4</option>
</select><br/>
<button onclick="test(this.form.SELECTSIZE);">TEST</button><br/>
<br/>
<button onclick="Element.update('test1', decodeURIComponent($('test1').innerHTML));">
DECODE</button>
</form>
<div id="test1"></div>
<div id="test2"></div>
</body>
</html>

*1:要素の集合であるかを判定するため、lengthプロパティの有無を確認しているのですが、selectタグ要素もlengthプロパティを持ちます。このため、更にtypeプロパティの有無を確認しています。妥当かどうか分かりませんが・・・。

*2:document.forms['フォーム名'].elements['要素名']でももちろん可