Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appended text not showing in d3 v4

I am trying to "translate" parallel coordinates example to the new version of d3 v4. I have a working example with this javascript (which additionally is a good example if anyone is trying to work with the v4 of d3 and has issues with the new functions):

var margin = {top: 30, right: 10, bottom: 10, left: 10},
    width = 600 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

var x = d3.scaleBand().rangeRound([0, width]).padding(1),
    y = {},
    dragging = {};


var line = d3.line(),
    //axis = d3.axisLeft(x),
    background,
    foreground,
    extents;

var svg = d3.select("#body").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 + ")");

d3.csv("cars.csv", function(error, cars) {
  // Extract the list of dimensions and create a scale for each.
    //cars[0] contains the header elements, then for all elements in the header
    //different than "name" it creates and y axis in a dictionary by variable name
  x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
    if(d == "name") {
        return false;
    }
    return y[d] = d3.scaleLinear()
        .domain(d3.extent(cars, function(p) { 
            return +p[d]; }))
        .range([height, 0]);
  }));

  extents = dimensions.map(function(p) { return [0,0]; });

  // Add grey background lines for context.
  background = svg.append("g")
      .attr("class", "background")
    .selectAll("path")
      .data(cars)
    .enter().append("path")
      .attr("d", path);

  // Add blue foreground lines for focus.
  foreground = svg.append("g")
      .attr("class", "foreground")
    .selectAll("path")
      .data(cars)
    .enter().append("path")
      .attr("d", path);

  // Add a group element for each dimension.
  var g = svg.selectAll(".dimension")
      .data(dimensions)
    .enter().append("g")
      .attr("class", "dimension")
      .attr("transform", function(d) {  return "translate(" + x(d) + ")"; })
      .call(d3.drag()
        .subject(function(d) { return {x: x(d)}; })
        .on("start", function(d) {
          dragging[d] = x(d);
          background.attr("visibility", "hidden");
        })
        .on("drag", function(d) {
          dragging[d] = Math.min(width, Math.max(0, d3.event.x));
          foreground.attr("d", path);
          dimensions.sort(function(a, b) { return position(a) - position(b); });
          x.domain(dimensions);
          g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
        })
        .on("end", function(d) {
          delete dragging[d];
          transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
          transition(foreground).attr("d", path);
          background
              .attr("d", path)
            .transition()
              .delay(500)
              .duration(0)
              .attr("visibility", null);
        }));
  // Add an axis and title.
  g.append("g")
      .attr("class", "axis")
      .each(function(d) {  d3.select(this).call(d3.axisLeft(y[d]));})
      //text does not show up because previous line breaks somehow
    .append("text")
      .style("text-anchor", "middle")
      .attr("y", -9)
      .text(function(d) { return d; });

  // Add and store a brush for each axis.
  g.append("g")
      .attr("class", "brush")
      .each(function(d) {
        d3.select(this).call(y[d].brush = d3.brushY().extent([[-8, 0], [8,height]]).on("brush start", brushstart).on("brush", brush_parallel_chart));
      })
    .selectAll("rect")
      .attr("x", -8)
      .attr("width", 16);
});

function position(d) {
  var v = dragging[d];
  return v == null ? x(d) : v;
}

function transition(g) {
  return g.transition().duration(500);
}

// Returns the path for a given data point.
function path(d) {
  return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}

function brushstart() {
  d3.event.sourceEvent.stopPropagation();
}


// Handles a brush event, toggling the display of foreground lines.
function brush_parallel_chart() {    
    for(var i=0;i<dimensions.length;++i){
        if(d3.event.target==y[dimensions[i]].brush) {
            extents[i]=d3.event.selection.map(y[dimensions[i]].invert,y[dimensions[i]]);

        }
    }

      foreground.style("display", function(d) {
        return dimensions.every(function(p, i) {
            if(extents[i][0]==0 && extents[i][0]==0) {
                return true;
            }
          return extents[i][1] <= d[p] && d[p] <= extents[i][0];
        }) ? null : "none";
      });
}

Everything works correctly (brushing on each of the axes, changing the order of the axes...) except that the labels on top of each of the axes are not showing, even though the text tag is appended and the text is written in the tag (can be checked by inspecting the resulting html using the browser). I believe that the code responsible for this is this part, but I am not capable of finding the reason why this happens:

  g.append("g")
      .attr("class", "axis")
      .each(function(d) {  d3.select(this).call(d3.axisLeft(y[d]));})
      //text does not show up because previous line breaks somehow
    .append("text")
      .style("text-anchor", "middle")
      .attr("y", -9)
      .text(function(d) { return d; });

Why do the text labels not show?

like image 322
Marc Cayuela Avatar asked Jan 03 '17 20:01

Marc Cayuela


1 Answers

In D3 v4 the axis component will explicitly set the fill to none on the selection it is called for. From the source code:

selection.filter(entering)
    .attr("fill", "none")      // <=== Fill set to none by D3
    .attr("font-size", 10)
    .attr("font-family", "sans-serif")
    .attr("text-anchor", orient === right ? "start" : orient === left ? "end" : "middle");

This will create the following code:

enter image description here

in contrast to the code generated by v3:

enter image description here

The <text> elements will inherit these properties, because they are children of these groups, whereby hiding the texts.

To show the labels you need to explicitly style them, which can be done

  1. Via CSS:

    .axis text {
      fill:black;   /* <== Set the fill */
      text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
      cursor: move;
    }
    

    Working demo.

  2. By setting the fill attribute on the <text> element itself:

    .append("text")
      .attr("fill", "black")          // <=== Set the fill
    //  .style("fill", "black")       // Will also work when using .style()
      .style("text-anchor", "middle")
      .attr("y", -9) 
      .text(function(d) { return d; });
    

    Working demo.

like image 54
altocumulus Avatar answered Nov 13 '22 14:11

altocumulus