JavaScriptだけでExcel風にサクサク編集できるスプレッドシートをカスタマイズしてみた

#はじめに
今回jexcel.jsでやってみたことは以下の通りです。
・スプレッドシートの表示
・スプレッドシートのオプション・イベントの設定
・カラムのタイプの設定
・セルに入力候補を表示するカスタマイズ

# スプレッドシートの表示

jexcel.jsはCSSとJSどちらもCDNで使用できます。
jexcel.jsの利用にはjsuites.jsが必須になります。
今回はjQueryで書いたので、jQueryも読み込みました。

```HTML:index.html
<!-- CSS -->
<link rel="stylesheet" href="https://bossanova.uk/jexcel/v4/jexcel.css" type="text/css" />
<link rel="stylesheet" href="https://bossanova.uk/jsuites/v3/jsuites.css" type="text/css" />
```
```HTML:index.html
<!-- JavaScript -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://bossanova.uk/jexcel/v4/jexcel.js"></script>
<script src="https://bossanova.uk/jsuites/v3/jsuites.js"></script>
<script src="js/script.js"></script>
```

HTML側に必要なのは、スプレッドシートを挿入したい場所にIDを付与したdivを置くだけ！

```HTML:index.html
<body>
    <h1>jExcelTEST</h1>
    <div id="spreadsheet"></div>
</body>
```

JSはオプションをJSONで記述してIDを付与したdivに対してメソッドを呼ぶだけ！

minDimensions（何マス×何マスで表示するか）を指定すれば、とりあえずスプレッドシートを表示することができます。

```javascript:script.js
var options = {
    minDimensions : [10,10],  //10×10
}
var table = $('#spreadsheet').jexcel(options);

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1119061/f2a54d16-460f-35f0-f67d-f7c4e0ca3000.png)


この状態で既に、入力、コピペ、オートフィル等エクセルライクに編集ができます。

#スプレッドシートのオプション・イベントの設定

JSONにオプションのパラメータを追加していけば、スプレッドシート全体の各種設定ができます。

```javascript:script.js
var options = {
  minDimensions : [10,10],
  columnSorting:false,                // ヘッダーをダブルクリックしてソートする機能の無効化
  allowInsertRow:false,               // 行を挿入を禁止
  allowManualInsertRow:false,         // ユーザーによる行の挿入を禁止
  allowInsertColumn:false,            // 列の挿入を禁止
  allowManualInsertColumn:false,      // ユーザーによる列の挿入を禁止
  allowDeleteRow:false,               // 行の削除を禁止
  allowDeleteColumn:false,            // 列の削除を禁止
  selectionCopy: false,               // 後述のチェックボックスなどの選択はコピー禁止（文字列・数値のコピーはできる）
}
var table = $('#spreadsheet').jexcel(options);
```

こんな感じでパラメータを追加すれば、編集に制限をつけたスプレッドシートも簡単にカスタマイズできます。
上記のパラメータはごく一部ですが、
これ以外にも、公式の[クイックリファレンス](https://bossanova.uk/jspreadsheet/v4/docs/quick-reference)の3.INITIALIZATIONに全部書いてあります。

また、イベントを使えば、より細かな制御もできます。
例えば、偶数列のみコピペを禁止したいときは

```javascript:script.js
var options = {
    minDimensions:[10,10],
    onbeforepaste:function(instance, cell, x, y, value){
      if(x % 2 == 0){
        return false
      }
    }
  }
var table = $('#spreadsheet').jexcel(options);
```
ペーストの前に呼ばれるイベントを定義して、列を判定して貼り付けを禁止できます。
このイベント戻り値は、ペーストされる値の指定でfalseにすると貼り付けされません。

これ以外のイベントも公式の[クイックリファレンス](https://bossanova.uk/jspreadsheet/v4/docs/quick-reference)の2.EVENTSに全部書いてあります。

# カラムのタイプの設定

jexcelには文字列以外の型も用意されています。

各列のtypeを指定することで、型の異なる文字の入力を禁止できます。

```javascript:script.js
var columns = [
  {type:'checkbox',width:30},                               //A列：チェックボックス：幅30px指定
  {type:'numeric',mask:'#,##.00',decimal:'.',width:100},    //B列：数値：三桁ごとにカンマ、少数第二位まで(以下切り捨て)、少数点は'.'を指定：幅100px指定
  {type:'text',,width:100},                                 //C列：テキスト:幅100px指定
  {type:'calendar',,width:100},                             //D列：日時:幅100px指定
  {type:'numeric', mask:'##',,width:100}                    //E列：数値：カンマなし、整数のみ:幅100px指定
 ]
var options = {
    minDimensions:[5,10],
    columns:columns
  }
var table = $('#spreadsheet').jexcel(options);
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1119061/7e865176-ac21-5752-f3ab-d5fa311b6742.png)

数値も桁数や表記を細かく設定できるし、日付指定にカレンダーからの入力もできて、とても便利です。

セレクトボックスや、色の選択、さらに他のライブラリとの連携でもっとカスタムできるみたいです([参照](https://bossanova.uk/jspreadsheet/v4/examples/column-types))

#セルに入力候補を表示するカスタマイズ

これが、この記事で一番書きたかったこと！
自由文入力もできるし、候補からも選択できる、HTMLの```<datalist>```みたいなことがしたかったのですが、
jexcelには、そんな型は用意されてませんでした。

ちなみに有償版だとセレクトボックスのオプションを追加する機能があるみたいです([参照](https://jexcel.net/v7/examples/dropdown-and-autocomplete))

でも今回は、型はtextのままで、入力候補を表示したい！という要件なので、こちらも不適でした。

jexcelのセルにdatalistの入力候補を表示させてみることにしました。

HTML側には表示させたい```<datalist>```を書きます。

```HTML:index.html
<body>
    <h1>jExcelTEST</h1>
    <div id="spreadsheet"></div>
</body>
<datalist id="data_list">
  <option value="apple"></option>
  <option value="orange"></option>
  <option value="lemon"></option>
  <option value="melon"></option>
</datalist>
```

JS側では、focusイベントをスプレッドシートのセル(td)の中のinputに加えました。
イベント内ではinput要素にlist属性を付与するだけの一般的なdatalistの使い方と同じです。

```javascript:script.js
var options = {
minDimensions:[10,10],
}

var table = $('#spreadsheet').jexcel(options);

$(document).on('focus','.jexcel tbody td input',function(){
      $(this).attr('list',`data_list`)
})
```

これでセルをフォーカスすると、```<datalist>```に定義した入力候補が出現
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1119061/cfd1c509-04b3-6b65-ab09-8748ca7634ce.png)

文字入力をしても該当する候補だけに絞られて表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1119061/1a97127c-c257-e357-2da8-bcba4abc97a8.png)

もちろん候補を選択すると、textとしてセルに入力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1119061/0308fa41-e957-9e9a-3ad2-ccd03abcaa23.png)



この機能の仕組みですが、
jexcelはセル(td)にフォーカスすると```<input type="text">```がセルの中に作られて、
フォーカスをはずすと、inputのvalueをセルのtextにして
inputを削除するっていう仕組みになっているので(jexcel.js1832行目~参照)
inputが存在するタイミングで、list属性を付与することで、入力候補を表示できています。

#注意
iOS端末では一部datalistの表示ができないブラウザがあるみたいです。
元も子もないですが、その場合は入力候補の表示ができません。
参考：https://mamewaza.com/support/blog/datalist.html

#最後に
ブラウザ依存しない、もっとスマートな方法があったら教えてほしいです！