Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3.js trying to implement sortable SVG table

As my title states I am trying to implement an interactive table with nice transitions, that's why I choose D3.js in combination with SVG elements.

I managed to implement a sortable table with normal HTML elements (th,tr,td):

http://jsfiddle.net/recek/q6LE6/

// create the table header
var thead = d3.select("thead").selectAll("th")
    .data(d3.keys(jsonData[0]))
    .enter().append("th").text(function(d){return d;})
    .on("click", function(d){ return refreshTable(d);});

// fill the table   
// create rows
var tr = d3.select("tbody").selectAll("tr").data(jsonData); 
tr.enter().append("tr");

// create cells
var td = tr.selectAll("td").data(function(d){return d3.values(d);});    
td.enter().append("td")
    .text(function(d) {return d;});

//update?
if(sortOn !== null) {           
        // update rows
        if(sortOn != previousSort){
            tr.sort(function(a,b){return sort(a[sortOn], b[sortOn]);});
            previousSort = sortOn;
        }
        else{
            tr.sort(function(a,b){return sort(b[sortOn], a[sortOn]);});
            previousSort = null;
        }

        //update cells
        td.text(function(d) {return d;});
}

As you can see the table sorts the data correctly when clicking on the header elements. Based on the above table I started to implement the svg version of this table and this is how I got so far:

http://jsfiddle.net/recek/v58zT/

// create the table header  
var header = headerGrp.selectAll("g")
    .data(d3.keys(jsonData[0]))
  .enter().append("g")
    .attr("class", "header")
    .attr("transform", function (d, i){
        return "translate(" + i * fieldWidth + ",0)";
    })
    .on("click", function(d){ return refreshTable(d);});

header.append("rect")
    .attr("width", fieldWidth-1)
    .attr("height", fieldHeight);

header.append("text")
    .attr("x", fieldWidth / 2)
    .attr("y", fieldHeight / 2)
    .attr("dy", ".35em")
    .text(String);

// fill the table   
// select rows
var rows = rowsGrp.selectAll("g.row").data(jsonData);

// create rows  
var rowsEnter = rows.enter().append("svg:g")
    .attr("class","row")
    .attr("transform", function (d, i){
        return "translate(0," + (i+1) * (fieldHeight+1) + ")";
    });

// select cells
var cells = rows.selectAll("g.cell").data(function(d){return d3.values(d);});

// create cells
var cellsEnter = cells.enter().append("svg:g")
    .attr("class", "cell")
    .attr("transform", function (d, i){
        return "translate(" + i * fieldWidth + ",0)";
    });

cellsEnter.append("rect")
    .attr("width", fieldWidth-1)
    .attr("height", fieldHeight);   

cellsEnter.append("text")
    .attr("x", fieldWidth / 2)
    .attr("y", fieldHeight / 2)
    .attr("dy", ".35em")
    .text(String);

//update if not in initialisation
if(sortOn !== null) {
        // update rows
        if(sortOn != previousSort){
            rows.sort(function(a,b){return sort(a[sortOn], b[sortOn]);});           
            previousSort = sortOn;
        }
        else{
            rows.sort(function(a,b){return sort(b[sortOn], a[sortOn]);});
            previousSort = null;
        }
        rows.transition()
            .duration(500)
            .attr("transform", function (d, i){
                return "translate(0," + (i+1) * (fieldHeight+1) + ")";
            });

        //update cells
        rows.selectAll("g.cell").select("text").text(String);
}

The problem Iam currently not able to solve, is that the sorting doesn't quite work. When clicking on the headers something happens, but the rows are not sorted correctly. What seems very strange to me is, that when inspecting the html elements in the browser, the g.row elements are sorted correctly according to the data bound to them.

I don't think there is a problem with my sort function, because Iam using the same one in both tables. My guess is that the newly sorted rows are not feeded with the right cells, but I don't know how to solve this problem.

EDIT: Ok I managed to update the text in the cells to the new sorting order correctly. The corrected code is on jsfiddle and also edited here. But there remains still one problem I just don't understand:

The transition of the rows to their new positions don't match the newly sorted data bound to them. For example when clicking multiple times on "id" you will see what I mean. Is it possible that the paramater "i", which I use to transition to the new positions doesn't represent the newly sorted order? I found this example, which is similar to mine and the transition works correctly:

http:// examples.oreilly.com/0636920026938/chapter_10/10_delay.html (sorry, cant post more than 2 links yet)

Where is the error in my code?

like image 697
Recek Avatar asked May 21 '14 20:05

Recek


1 Answers

Found the solution to my problem on this page:

http://bost.ocks.org/mike/constancy/

The problem was a missing key function for the data to be correctly bound to the rows.

By changing this line:

var rows = rowsGrp.selectAll("g.row").data(jsonData);

to

var rows = rowsGrp.selectAll("g.row").data(jsonData, 
    function(d){ return d.id; });

and removing the cell update after the transition:

//update cells
rows.selectAll("g.cell").select("text").text(String);

The table sorts and transitions correctly now.

like image 184
Recek Avatar answered Oct 07 '22 16:10

Recek