[JavaScript] d3.jsのupdate, enter, exit

2015/08/1

こんにちは。きんくまです。

引き続きd3.jsです。

今回はデータと同期するためのupdate, enter, exitについてです。

参考リンク
>> Binding data
>> Let’s Make a Bar Chart
>> Three Little Circles

例えば[1,2,3]というデータがあったとして、それをもとにしたグラフを作るとします。
htmlのbodyの中身は空だとします。
まず、どこにそのデータをいれるかを選択します。

d3.select('body').selectAll('div');

htmlのbodyが空なので、selectAllでdivを選択しても空なのですが、d3に対象を始めに教えてあげる必要があるためにまずselectします。

次にデータの同期を開始します。

var bardata = [1,2,3];
d3.select('body').selectAll('div')
    .data(bardata);

これでデータの同期ができました。ただ、同期するべきdivの個数が0なのでまだ何もすることができません。

この対象となるhtmlやsvgの要素数とデータの個数に差がでてきたときに必要なメソッドがenterとexitです。
enterは対象がデータの個数より少ないときに
exitは対象がデータの個数より多いときに
使います。差分が出た時という感じ。

var bardata = [1,2,3];
d3.select('body').selectAll('div')
    .data(bardata)
    .enter('div')
    .text('hello');

とやると3つのhelloが表示されます。同期したデータを使いたいときには関数を引数に入れればOKです。

var bardata = [1,2,3];
d3.select('body').selectAll('div')
    .data(bardata)
    .enter('div')
    .text(function(d){ return 'data = ' + d; });

同じようにexitもできます。

これをふまえて作ってみたデモ

今回作ったもの。最初のデータが[1, 3, 5, 7]で、棒グラフで表します。
+-ボタンを押すとその配列にデータを追加したり、削除したりします。
それをグラフで同期します。

html

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>D3 Sample</title>
    <style>
        html, body {
            margin: 0;
        }
        body{
            background: white;
        }
        #container{
            padding: 15px;
            position: relative;
            -moz-user-select: none;
            -webkit-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
        .bar{
            position: absolute;
            width:20px;
            background-color: steelblue;
            font-size: 10px;
            text-align: center;
            color:white;
        }
        .push_button{
            border:1px solid #ccc;
            background-color: #eaeaea;
            text-align: center;
            width:60px;
            height: 30px;
            box-sizing: border-box;
            padding-top: 3px;
            font-size: 16px;
            cursor: pointer;
            display:inline-block;
        }
        .push_button:first-child{
            margin-right: 10px;
        }
        .button_area{
            padding: 10px;
        }
    </style>
</head>
<body>
<div class="button_area">
    <div id="minus_button" class="push_button">-</div>
    <div id="add_button" class="push_button">+</div>
</div>
<div id="container"></div>

<script src="js/d3.js"></script>
<script src="js/linechart.js"></script>
</body>
</html>

TypeScript

/// <reference path="definitions/d3.d.ts" />

module sample {
    var dataset = [1, 3, 5, 7];

    export class LineChart{
        count = 0;
        stepNum = 2;
        barWidth = 20;
        barHeightRatio = 10;

        constructor(){
            this.count = dataset[dataset.length - 1];

            this.appendDiv();
            d3.select('#add_button').on('click', ()=>{
                this.count = this.count + this.stepNum;
                dataset.push(this.count);
                this.appendDiv();
                this.updateDiv(); //ここ!
            });
            d3.selectAll('#minus_button').on('click', ()=>{
                this.count = this.count - this.stepNum;
                dataset.pop();
                this.removeDiv();
                this.updateDiv(); //ここ!
            });
        }

        getBarStyle():any{
            var maxVal = dataset[dataset.length - 1];
            return {
                height:(d:any)=>{ return d * this.barHeightRatio + 'px';},
                left:(d:any, i:number)=>{ return i * (this.barWidth + 2) + 'px';},
                top:(d:any)=>{ return (maxVal - d) * this.barHeightRatio + 'px';}
            };
        }

        updateDiv(){
            d3.select('#container')
                .selectAll('div')
                .data(dataset)
                .style(this.getBarStyle());
        }

        appendDiv(){
            d3.select('#container')
                .selectAll('div')
                .data(dataset)
                .enter()
                .append('div')
                .attr('class', 'bar')
                .text((d)=>{ return d; })
                .style(this.getBarStyle());
        }

        removeDiv(){
            d3.select('#container')
                .selectAll('div')
                .data(dataset)
                .exit()
                .remove();
        }
    }
}

var linechartMain = new sample.LineChart();

ボタンを押したあとにupdateDivを最後に呼んでいます。もしこれを呼ばないと、個数に差がでいない部分は更新されずそのままです。なので、こんな感じに表示されてしまって、うまくいきませんでした。

linechart_fig

d3の基本が少しずつわかってまいりました!

LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

LINEスタンプ作りました!
毎日使える。とぼけたウサギ。LINEスタンプ販売中! 毎日使える。とぼけたウサギ

ページトップへ戻る