HandsonTableで偽EXCELやってみた

とりあえずHandsonTableとかいうJQUERYのライブラリがあるらしいんで
色々とやってみました。最終的にはEXCELチックにしたかったんですが
色の保存と枠の保存が未実装、さらにフィルタとかも不十分な状態ながら
ひとまずサンプルの成果ということで、コードは相変わらずへぼちんです。
色々なサンプルを参照させていただいてここまでこぎつけました。
先人の方々ありがとうございます。

HTML部分

<!DOCTYPE html>
<html>
<head>
<title>Handsontable - サンプル</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handsontable/0.29.1/handsontable.full.min.js"></script>
<script src="sample05.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/handsontable/0.29.1/handsontable.full.min.css">
</head>
<body>
<a id ="header">
 <div class="header_area">
     <div class="header_block">
         <div class="header_box">
      <h1>HandsontableDemoPage</h1>
      <input id="delData" type="button" value="ローカルストレージ内データ削除">
      <p>
      <input id="searchField" type="text" placeholder="文字列を入力してください。">
      <div id="resultCount">検索結果:0件</div>
      <p>
                    <label>セルの色:<input id="cellColorPicker" type="color" name = "cell" ></label>
                    <label>文字の色:<input id="fontColorPicker" type="color" name = "font" ></label>
      <p>
      <input id="saveButton" type="button" value="保存する">
      <input id="clearButton" type="button" value="クリア">
         </div>
     </div>
 </div>
</a>
<div class="contents_wrap">
    <div class="contents_area">
        <div class="content_block">
            <div id="grid"></div>
        </div>
    </div>
</div>
<div class="footer_area">
    <div class="footer_block">
        <div class="footer_box">
            <p>
                <a href ="#header">ページ上部に移動</a>
            </p>
        </div>
    </div>
</div>
</body>
</html>

JS

/*refer
  http://fiddle.jshell.net/anschwem/n3eoeqLd/10/light/
  https://docs.handsontable.com/pro/1.8.1/tutorial-introduction.html
  http://mm-s.biz/WordPress/2015/09/11/javascript-jquery%EF%BC%88%E7%AC%AC13%E5%9B%9E%EF%BC%89/
  http://qiita.com/opengl-8080/items/e756af444147771fab18
  https://cloudear.jp/blog/?p=483
  http://bl.ocks.org/anzfactory/b841ce8ad5cd70b2de8c
  https://anz-note.tumblr.com/post/133074040041/jsgoogle%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AE%E3%82%88%E3%81%86%E3%81%AA%E3%82%82%E3%81%AE%E3%82%92%E3%81%8A%E6%89%8B%E8%BB%BD%E3%81%AB%E3%81%A4%E3%81%8F%E3%82%8C%E3%81%A1%E3%82%83%E3%81%86handsontable
  http://www.go-next.co.jp/blog/web/javascript/17934/
  http://stackoverflow.com/questions/29785220/handsontable-change-colors-of-cells-rows-columns-with-color-picker
  https://gitlab.com/anupamme/jquery-handsontable
  http://stackoverflow.com/questions/16870280/why-custom-renderer-do-not-work-as-expected-and-cause-strange-table-cells-behavi
  http://himakan.net/web/js/markdown_table_editor_code
  
*/


//グローバル変数(HadsonTable用)
var grid, hot  //, mode, beforeBackgroundColor, beforeStyleColor ;
var earchResultCount = 0;

//ボタン設定
$(loaded);

function loaded() {
    if (window.localStorage) {
        showContent();
        // ボタンをクリックしたときに実行するイベントを設定する
        //ローカルストレージ内データ削除ボタン挙動

        $("#delData").click(
            function() {
                var myRet = confirm("PC内の投稿情報を全削除します。よろしいですか?");
                if (myRet) {
                    localStorage.clear();
                    clearContent();
                }
            });
        //保存ボタン挙動
        $("#saveButton").click(
            function() {
                saveContent();
            });
        //クリアボタン挙動
        $("#clearButton").click(
            function() {
                var myRet = confirm("全てのセルの内容をクリアします。よろしいですか?");
                if (myRet) {
                    clearContent();
                }
            });
    } else {
        alert("本アプリ未対応のブラウザです。対応ブラウザをご利用ください。");
    }
}

// ローカルストレージに保存した値をテーブルに表示する
function showContent() {
    var key, value = [];

    key = "gridData";
    value = localStorage.getItem(key);
    var data;

    if (value === "undefined") {
        data = [
            ["", "", "", ""]
        ];
    } else {
        data = JSON.parse(value);
    }

//セルの色変更
    var TableStyles = function(hot) {
        var self = this;
        var _cellStyles = [];
        var _createStyle = function(row, col, cellcolor ,fontcolor) {
            var style = {
                row: row,
                col: col,
                renderer: function(instance, td, row, col, prop, value, cellProperties) {
                    Handsontable.renderers.TextRenderer.apply(this, arguments);
                      td.style.color = fontcolor;
                      td.style.backgroundColor = cellcolor;
         },
            };
            return style;
        };

        self.getStyles = function() {
            return _cellStyles;
        };

        //セルのスタイル設定
        self.setCellStyle = function(row, col, cellcolor ,fontcolor, updateTable) {
            if (_cellStyles.length == 0) {
                _cellStyles.push(_createStyle(row, col, cellcolor ,fontcolor));
            } else {
                    _cellStyles.push(_createStyle(row, col, cellcolor ,fontcolor));
            }

      if (updateTable != false) {
                hot.updateSettings({
                    cell: self.getStyles()
                });
                hot.render();
            };
        };

        //選択範囲のセルに処理を行う。配列値が0開始なのでループ終了条件に+1している。
        self.setCellsStyle = function(startrow, endrow, startcol, endcol, cellcolor ,fontcolor) {
            for (var row = startrow; row < endrow + 1; row++) {
                for (var col = startcol; col < endcol + 1; col++) {
                    self.setCellStyle(row, col, cellcolor ,fontcolor, false);
                    hot.updateSettings({
                        cell: self.getStyles()
                    });
                }
            }
            hot.render();
        };
    };


    //HandsonTable設定
    grid = document.getElementById("grid");
    var settings = {
        data: data, //データ表示
        autoColumnSize: true, //カラム自動調整
        startRows: 10, //初期表示行数
        startCols: 5, //初期表示列数
        autoColumnSize: true, //カラム幅自動調整
        rowHeaders: true, //行ヘッダー
        colHeaders: true, //列ヘッダー
        columnSorting: true, //ソート
        sortIndicator: true, //ソートの矢印
        minSpareRows: 1, //1行だけの空白セル
        fillHandle: true, //possible values: true, false, "horizontal", "vertical" フィル有効
        manualColumnMove: true, //ドラッグで移動
        manualColumnResize: true, //ドラッグでサイズ調整(列) 
        manualRowResize: true, //ドラッグでサイズ調整(行)
        comments: true, //コメント(右クリックメニュー)
        mergeCells: true, //セル結合(右クリックメニュー)
        customBorders: true, //罫線(右クリックメニュー)
        //検索で色付有効
        search: {
            callback: searchResultCounter
        }
    };

    hot = new Handsontable(grid, settings);
    var styles = new TableStyles(hot);

    //右クリックメニュー
    hot.updateSettings({
        contextMenu: {
            //独自メニュー実装
            callback: function(key, cell, e) {
                var text = '';
  var mode ='';
                var sel = hot.getSelected();
                var startRow = sel[0]; //開始行
                var startCol = sel[1]; //開始列
                var endRow = sel[2]; //終了行
                var endCol = sel[3]; //終了列
  var cellColor = document.getElementById("cellColorPicker").value;
  var fontColor = document.getElementById("fontColorPicker").value;

                // 独自メニューのクリック判定
                switch (key) {
                    case 'setColor':
             //色設定
                    styles.setCellsStyle(startRow, endRow, startCol, endCol, cellColor , fontColor);
                        break;
                }
            },

            //メニュー
            items: {
                row_above: {
                    name: '上に行を挿入'
                },
                row_below: {
                    name: '下に行を挿入'
                },
                col_left: {
                    name: '左に列を挿入'
                },
                col_right: {
                    name: '右に列を挿入'
                },
                remove_row: {
                    name: '選択行を削除',
                    disabled: function() {
                        return $("#grid").handsontable('countRows') <= 2;
                    }
                },
                remove_col: {
                    name: '選択列を削除',
                    disabled: function() {
                        return $("#grid").handsontable('countCols') <= 1;
                    }
                },
                'setColor': {
                    name: "色を変更"
                },
                undo: {
                    name: '元に戻す'
                },
                redo: {
                    name: 'やり直す'
                },
                make_read_only: {
                    name: 'セルを読取専用にする'
                },
                mergeCells: {
                    name: 'セルを結合する'
                },
                alignment: {
                    name: '文字位置指定'
                },
                borders: {
                    name: '枠線'
                },
                commentsAddEdit: {
                    name: 'コメントの挿入'
                },
                commentsRemove: {
                    name: 'コメントの削除'
                }
            }
        },
    });

    //検索結果合致で色を付ける
    var searchField = document.getElementById("searchField");
    var resultCount = document.getElementById("resultCount");

    Handsontable.Dom.addEvent(searchField, 'keyup', function(event) {
        searchResultCount = 0;
        var queryResult = hot.search.query(this.value);
        resultCount.innerText = "検索結果:" + searchResultCount.toString() + "件";
        hot.render();
    });

}

// テーブルの内容をローカルストレージに保存する
function saveContent() {
    var content = hot.getData();
    var key = "gridData";
    var val = JSON.stringify(content);
    localStorage.setItem(key, val);
    alert("データを保存しました。");
    hot.render();
}

//テーブルの内容をクリアする
function clearContent() {
    hot.clear();
}

//検索結果件数を取得する
function searchResultCounter(instance, row, col, value, result) {
    Handsontable.Search.DEFAULT_CALLBACK.apply(this, arguments);
    if (result) {
        searchResultCount++;
    }
}

●残課題としては下記の内容かなぁと
 ・検索にヒットしたレコードのみをレコード表示する。
 ・検索条件クリアしたらそのデータを再表示する。
 ・列の枠サイズを取得して保存する。
 ・行のサイズを取得して保存する。
 ・コメントの内容を取得して保存する。
 ・着色した色データを保存する。
 ・検索ヘッダー領域を固定する。

これだけできればまぁ一通りの業務アプリのフロントエンド要件は
満たせそうだけど。扱いが結構厄介でした。
ローカルストレージではなくデータベースと組み合わせたら楽なのかもしれないけど・・・。

コメント

このブログの人気の投稿

証券外務員1種勉強(計算式暗記用メモ)

GASでGoogleDriveのサブフォルダとファイル一覧を出力する

マクロ経済学(IS-LM分析)