As my title states I am trying to implement an interactive table with nice transitions, that's why I choose D3.js in bination with SVG elements.
I managed to implement a sortable table with normal HTML elements (th,tr,td):
/
// 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:
/
// 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/0636920026938/chapter_10/10_delay.html (sorry, cant post more than 2 links yet)
Where is the error in my code?
As my title states I am trying to implement an interactive table with nice transitions, that's why I choose D3.js in bination with SVG elements.
I managed to implement a sortable table with normal HTML elements (th,tr,td):
http://jsfiddle/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/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./0636920026938/chapter_10/10_delay.html (sorry, cant post more than 2 links yet)
Where is the error in my code?
Share Improve this question edited Oct 31, 2017 at 18:15 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked May 21, 2014 at 20:06 RecekRecek 1,38815 silver badges18 bronze badges 2- I'm not sure if SVG is a better choice than HTML for a table. For mostly text, HTML is much more convenient. Here is an example of a sortable HTML table with transitions. – Lars Kotthoff Commented May 22, 2014 at 9:42
- This might be right, but Iam planing on doing some more interaction with the table than just sorting and I think to get nicer animation using svg. But thank you for the example, when Iam getting stuck with the svg methods to often I will reconsider using HTML ;) – Recek Commented May 22, 2014 at 13:02
1 Answer
Reset to default 7Found the solution to my problem on this page:
http://bost.ocks/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.