LoginSignup
0
1

More than 3 years have passed since last update.

ブラウザーで表計算を作りたい?

Posted at

ブラウザーでも表計算みたいに入力したい

…とかいう要望が出ることがありますね。
やってみましょう。
テキストボックスをたくさん並べて…、じゃないですよ。

worksheet.html
<style>
    *{
        word-break:break-all;
        word-wrap:break-word;
        overflow-wrap:break-word;
        box-sizing:border-box;
    }
    #worksheet{width:100%;border-collapse:collapse;}
    #worksheet th{background-color:gainsboro;border-right:solid 1px gray;border-bottom:solid 1px gray;}
    #worksheet thead tr th{}
    #tbody tr th{width:64px;}
    #tbody tr td{border:solid 1px gray;font-size:16px;font-family:sans-serif;white-space:pre;}
    #textarea{position:absolute;background-color:lemonchiffon;font-size:16px;font-family:sans-serif;}
</style>

<table id="worksheet">
    <thead id="thead">
        <tr>
            <th></th>
            <th>A</th>
            <th>B</th>
            <th>C</th>
        </tr>
    </thead>
    <tbody id="tbody">
        <tr>
            <th>1</th>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <th>2</th>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <th>3</th>
            <td></td>
            <td></td>
            <td></td>
        </tr>
        <tr>
            <th>4</th>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
<table>
<textarea id="textarea"></textarea>

テキストボックスはひとつだけです。入力する位置へ動かすのです。
表は、動的に生成しても良いですね。

機能部分

これで各セルに入力できるようになります。
あとは、ファイルに保存とか、データベースから…とか、計算式を…とか、好きにやってください。
カラム幅の指定とか、行固定のスクロールとか、凝り始めたらキリがないですね。
行の追加・削除くらいは欲しいかな。

worksheet.js
    var pos;
    var textarea;
    var table;
    var thead;
    var tr;
    var tbody;

    window.addEventListener('DOMContentLoaded',function(event)
    {
        table=document.getElementById('worksheet');
        thead=document.getElementById('thead');

        // セルサイズの固定化
        tr=document.getElementById('thead').children[0];
        for(i=0;i<tr.children.length;i++){
            tr.children[i].style.width=tr.children[i].offsetWidth+'px';
            tr.children[i].style.height=tr.children[i].offsetHeight+'px';
        }

        // 編集位置の初期設定
        tbody=document.getElementById('tbody');
        td=tbody.children[0].children[1];
        textarea=document.getElementById('textarea');
        pos=setpos(textarea,td,table);

        // クリックした位置に移動
        table.addEventListener('click',
            function(event){
                pos=setpos(textarea,event.srcElement,table);
                td=event.srcElement;
            },
        false);

        // リサイズに合わせてリサイズ
        window.addEventListener('resize',
            function(event){
                pos=setpos(textarea,pos['td'],table);
            },
        false);

        // 矢印キーで移動
        document.addEventListener('keydown',onkeydown,false);
    },false);

    function setpos(textarea,td,table)
    {
        if(textarea && pos){
            if(pos['td']){
                pos['td'].innerHTML=textarea.value;
            }
        }
        if(td && table){
            pos={
                "top"   :td.offsetTop+table.offsetTop+1,
                "left"  :td.offsetLeft+table.offsetLeft+1,
                "width" :td.offsetWidth-1,
                "height":td.offsetHeight-1,
                "col"   :td.cellIndex,
                "row"   :td.parentNode.rowIndex,
                "td"    :td };
            if(navigator.userAgent.match(/Firefox/)){
                pos.top-=2;
                pos.left-=1;
            }
        }
        if(textarea){
            if(td){
                textarea.value=td.innerHTML;

                textarea.style.top      =pos['top']+"px";
                textarea.style.left     =pos['left']+"px";
                textarea.style.width    =pos['width']+"px";
                textarea.style.height   =pos['height']+"px";
            }
        }
        return pos;
    }

    function onkeydown(event)
    {
        if(event.target==textarea){
            switch(event.keyCode){
            case 10:    // return
                break;
            case 13:    // enter
                if(event.ctrlKey
                || event.shiftKey
                || event.altKey){       // 改行を入力させる
                    break;
                }else{
                    textarea.blur();
                }
                break;
            }
        }else{
            switch(event.keyCode){
            case 38:
            case 40:
            case 37:
            case 39:
                if(event.keyCode==38){          // ↑
                    pos['row']--;
                    if(pos['row']<1){
                        pos['row']=1;
                    }
                }else if(event.keyCode==40){    // ↓
                    pos['row']++;
                    if(tbody.children.length<pos['row']){
                        pos['row']=tbody.children.length;
                    }
                }else if(event.keyCode==37){    // ←
                    pos['col']--;
                    if(pos['col']<=0){
                        pos['col']=1;
                    }
                }else if(event.keyCode==39){    // →
                    pos['col']++
                    if(tr.children.length<=pos['col']){
                        pos['col']=tr.children.length-1;
                    }
                }
                newtd=tbody.children[ pos['row']-1 ].children[ pos['col'] ];
                pos=setpos(textarea,newtd,table);
                break;
            case 10:    // return
                break;
            case 13:    // enter
                textarea.focus();
                event.preventDefault();         // 改行を入力させない
                event.returnValue = false;      // ie
            default:
                break;
            }
        }
    }
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1