konrad
konrad

Reputation: 3706

bar chart transition, strange behavior d3.js

I am curious if someone can point out what I am doing wrong here? I was hoping my code would generate a smooth transition on click events, instead, first click only shifts the x-axis labels (why? how do i fix that?), then second/third do the sorting. Is there a way to prevent that from happening? I want to sort on first click and sure it would be nice if my labels didn't jump around. What am I missing here?

http://jsbin.com/cohaziqugo/edit?js,output

var data =  [{"name":"A","value":0.08167},{"name":"B","value":0.01492},{"name":"C","value":0.0278},{"name":"D","value":0.04253},{"name":"E","value":0.12702},{"name":"F","value":0.02288},{"name":"G","value":0.02022},{"name":"H","value":0.06094},{"name":"I","value":0.06973},{"name":"J","value":0.00153},{"name":"K","value":0.00747},{"name":"L","value":0.04025},{"name":"M","value":0.02517},{"name":"N","value":0.06749},{"name":"O","value":0.07507},{"name":"P","value":0.01929},{"name":"Q","value":0.00098},{"name":"R","value":0.05987},{"name":"S","value":0.06333},{"name":"T","value":0.09056},{"name":"U","value":0.02758},{"name":"V","value":0.01037},{"name":"W","value":0.02465},{"name":"X","value":0.0015},{"name":"Y","value":0.01971},{"name":"Z","value":0.00074}];

var tickValues = data.map(function (d){return d.name;});
var step = Math.floor(tickValues.length / 24);
var indexes = d3.range(0,tickValues.length, step);
if (indexes.indexOf(tickValues.length - 1) == -1){
    indexes.push(tickValues.length - 1);
}
var tickArray = d3.permute(tickValues, indexes);

var margin = { top: 40, right: 20, bottom: 30, left: 40 },
    width = 1500 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .domain(data.map(function (d) { return d.name; }))
    .rangeBands([0, width], 0.1, 0.35);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickValues(tickArray);

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var barChart = d3.select("#barbarchart1").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    y.domain([0, 0.2]);

barChart.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .selectAll("text")
      .style("text-anchor", "end")
      .attr("dx", "-0.8em")
      .attr("dy", "0.15em")
      .attr("transform", function(d){
          return "rotate(-65)"
      });

barChart.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Test");

barChart.selectAll("#bar")
    .data(data)
    .enter().append("rect")
    .attr("id", "bar")
    .attr("x", function (d) { return x(d.name); })
    .attr("width", x.rangeBand())
    .attr("y", function (d) { return y(d.value); })
    .attr("fill", "grey")
    .attr("height", function (d) { return height - y(d.value); })
    .on("click", function() {sortBars();})
    .on("mouseover", function(d){

        var xPos = parseFloat(d3.select(this).attr("x"));
        var yPos = parseFloat(d3.select(this).attr("y"));
        var height = parseFloat(d3.select(this).attr("height"));
        var width = parseFloat(d3.select(this).attr("width"));

        d3.select(this).attr("fill", "red");

        barChart.append("text")
            .attr("x",xPos)
            .attr("y", yPos - 3)
            .attr("font-family", "sans-serif")
            .attr("font-size", "10px")
            .attr("font-weight", "bold")
            .attr("fill", "black")
            .attr("text-anchor", "middle")
            .attr("id", "tooltip")
            .attr("transform", "translate(" + width/2 + ")")
            .text(d.name +": "+ d.value);
    })
    .on("mouseout", function(){
        barChart.selectAll("#tooltip").remove();
        d3.select(this).attr("fill", "grey");             
    });

var sortOrder = true;

var sortBars = function() {

    //Flip value of sortOrder
    sortOrder = !sortOrder;

    var x0 = x.domain(data.sort(sortOrder
      ? function(a, b) { return b.value - a.value; }
      : function(a, b) { return d3.ascending(a.name, b.name); })
      .map(function(d) { return d.name; }))
      .copy();

    barChart.selectAll("#bar")
      .sort(function(a, b) { return x0(a.name) - x0(b.name); });

    var transition = barChart.transition().duration(750),
        delay = function(d, i) { return i * 50; };

    transition.selectAll("#bar")
        .delay(delay)
        .attr("x", function(d) { return x0(d.name); });

    transition.select(".x.axis")
        .call(xAxis)
        .selectAll("text")
        .selectAll("g")
        .delay(delay)
    ;};

function type(d) {
    d.value = +d.value;
    return d;
}

Upvotes: 0

Views: 80

Answers (1)

Eric Guan
Eric Guan

Reputation: 15982

My solution: http://jsbin.com/cequrabuqi/edit?js,output

first click only shifts the x-axis labels

data's default sort is ascending. On first click, you flip sortOrder from true to false, causing your ternary condition to choose function(a, b) { return d3.ascending(a.name, b.name); }) as the sort order, which is still ascending. While the code is correct, it looks broken because the sort order never changes on the first click, thus nothing happens to the bars on screen.

would be nice if my labels didn't jump around.

I don't fully understand D3, but what seems to happen is when you call transition.select(".x.axis").call(xAxis), it removes any styles you have applied. So in my solution, i reapplied the styles each time the sort function is called.

Upvotes: 1

Related Questions