テーブルの td タグをクリックで input にする

Handsontable より、もっと手軽に自分で書く table タグの td をクリックで、input にして編集するものを
汎用的に作ってみたくなりました。

結果、以下のようなサンプルページです。

td をクリックした時に、数値なら右寄せ、3桁区切りのオプション

f:id:posturan:20160313190707j:plain



blur() でフォーカスが離れたら、callback関数を実行するオプション、さらに、指定するフィールドに値をセット

f:id:posturan:20160313190718j:plain



このようなもに動き、設定が楽に書けるものを考えました。

書式は、
  TableEdit.input( tdを指すセレクタ [ , option ] );

option は、Hash配列で、
type : で指定するものは、'text' , 'number' , 'digit' のいずれか。
   digit は、3桁区切りの数値
setfield : で、input type="hidden" など、jQueryObject.val()でセットできるものを指定。
callback : で、blur() 時、input に入っている値を引数にした callback関数を指定します。

tableedit.js として、以下のソースになります。

/**
 * tableedit.js
 */

$(function(){

TableEdit={};

TableEdit.input = function(td, options){
   $(td).click(edit_input(options));
};
function edit_input(options){
   return function(){
      var inputstyle = "margin-left: 3px; margin-right: 3px; width: 92%;";
      var type = "text";
      var _callbackfunc = null;
      var _setfield = null;
      if (typeof options=="object" && _isHash(options)){
         if (options['type'] != undefined){
            type = options['type'].toLowerCase();
            if (type=='number' || type=='digit'){
               inputstyle = "text-align: right;margin-left: 3px; margin-right: 3px; width: 92%;";
            }
         }
         if (options['callback'] != undefined){
            _callbackfunc = options['callback'];
         }
         if (options['setfield'] != undefined){
            _setfield = options['setfield'];
         }
      }
      if (type=='number' || type=='digit'){
         $(this).html($("<input>").attr("type","text").attr("style", inputstyle).val($(this).text().replace(/,/g, '')));
      }else{
         $(this).html($("<input>").attr("type","text").attr("style", inputstyle).val($(this).text()));
      }
      var paddingkeep = $(this).css("padding");
      $(this).css("padding", "0 0 0 0");
      $("input", this).focus().blur(function(){
         $(this).parent().attr("style", "padding:" + paddingkeep);
         if (type=='digit'){
            var str = $(this).val();
            str = str.replace(/[0-9]/g, function(s){ return String.fromCharCode(s.charCodeAt(0)-0xFEE0); });
            str = str.replace(/,/g, '');
            if (str.match(/^(|-)[0-9]+$/)){
               str = str.replace(/^[0]+([0-9]+)/g, '$1');
               str = str.replace(/([0-9]{1,3})(?=(?:[0-9]{3})+$)/g, '$1,');
            }
            $(this).after(str).unbind().remove();
         }else{
            $(this).after($(this).val()).unbind().remove();
         }
         if (typeof(_setfield) != "undefined"){
            $(_setfield).val($(this).val());
         }
         if (typeof(_callbackfunc) != "undefined"){
            _callbackfunc($(this).val());
         }
      });
      return false;
   };
}
function _isHash(obj){
   if (typeof obj=="object"){
      for(k in obj){
         if(obj[k] != undefined){
            return true;
         }else{
            return false;
         }
      }
   }
   return false;
}

});

サンプルHTMLは、以下のとおり。
テーブル複数行、tr のセレクタは、each関数で全行に効かせるようにします。
td 列に沿って、:eq(列インデックス)で指定します。


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sample</title>
<script type="text/javascript" src="../../js/jquery-2.0.3.min.js"></script>
<style type="text/css">
table.border-table{
   border: 1px #000000 solid;
   border-collapse: collapse;
   border-spacing: 0;
   white-space: nowrap;
}
.border-table th, .border-table td{ border: 1px #000000 solid; }
.edit td , .edit th {
   padding: 4px;
   padding-left : 10px;
   padding-right: 10px;
   padding-top: 4px;
   padding-bottom: 4px;
   width: 120px;
}
.edit td+td+td{
    text-align: right;
}
.edit td+td{
    text-align: right;
}
</style>
<script type="text/javascript" src="tableedit.js"></script>
<script type="text/javascript">
$(function(){
   $('#target > tbody > tr').each(function(i,trobj){
      TableEdit.input($(trobj).children('td:eq(0)'));
      TableEdit.input($(trobj).children('td:eq(1)'), { type: "number" });
      TableEdit.input($(trobj).children('td:eq(2)'),
         {   type: "digit",
            setfield: "#price",
            callback: function(e){ alert("price = "+e); }
         }
      );
   });

});
</script>
</head>
<body>
<h2>Table td click → input</h2>

<table id="target" class="border-table edit">
   <thead>
      <tr>
         <th>品名</th><th>数量</th><th>価格</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <td>ロクシタン</td><td>1200</td><td>120,000</td>
      </tr>
      <tr>
         <td>リードケース</td><td>24</td><td>48,000</td>
      </tr>
   </tbody>
</table>
<br/>
<input type="hidden" id="price" name="price">
</body>
</html>