score:1

Accepted answer
  • you add two labels to your axes with a class label
  • your selection svg.selectAll('.label') searches the whole svg and is based on .label.

That picks up those two labels, counting them as already created and thus irrelevant in the enter phase

The simplest fix is to wrap your selection in a g element, something like

var graph = svg.append("g");

graph.selectAll('.dot')
     .data(graphData)
     // ...

graph.selectAll('.label')
     .data(graphData)
     // ...

And a demo

var url = "https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json";

var m = {t: 20, r: 120, b: 30, l: 40},
    width = 800 - m.l - m.r,
    height = 700 - m.t - m.b;

var svg = d3.select("body").append("svg")
                              .attr("width", width + m.l + m.r)
                              .attr("height", height + m.t + m.b)
                            .append("g")
                              .attr("transform", "translate(" + m.l + "," + m.t + ")");

var div = d3.select('body')
            .append('div')
              .style({
                'position':'absolute',
                'text-align':'center',
                'width':'240px',
                'height':'2.5em',
                'font':'1.5em sans-serif',
                'color':'yellow',
                'background':'black',
                'border-radius':'8px',
                'border':'solid 1px green',
                'opacity':0
              });

var colorScale = d3.scale.ordinal()
                          .range(["#FF0000", "#009933"]);

var xScale = d3.scale.linear()
                .range([width, 0]);

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

var xAxis = d3.svg.axis()
                  .scale(xScale)
                  .orient("bottom")
                  .tickFormat(formatMinSec);

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


d3.json(url, callback);

function callback (error, data) {

    if (error) throw error;

    var bestTime = _.sortBy(data, 'Place')[0].Seconds;
  
    var graphData = _.map(data, (d)=> ({
        'secondsBehind': Math.abs(bestTime - d.Seconds),
        'year': d.Year,
        'nationality': d.Nationality,
        'doping': d.Doping,
        'dopingAllegations': d.Doping.length > 0 ? "Doping Allegations":"No Doping Allegations",
        'name': d.Name,
        'place': d.Place,
        'time': d.Time
    }) )
    
    var timeRange = d3.extent(graphData, (d) => d.secondsBehind );
    
    xScale.domain([timeRange[0]-15, timeRange[1]]);
  
    var rankRange = d3.extent(graphData, (d) => d.place );

    yScale.domain([rankRange[0], rankRange[1]+1]);

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
          .append("text")
          .text("Minutes : Seconds Behind Fastest Time")
          .attr({
            'class': 'label',
            'x': width,
            'y': -6
          })
          .style("text-anchor", "end");

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
          .text("Ranking")
          .attr({
            'class': 'label',
            "transform": "rotate(-90)",
            "y": 6,
            "dy":   ".71em"
          })
          .style("text-anchor", "end");

	 var graph = svg.append("g");

    graph.selectAll('.dot')
        .data(graphData)
        .enter()
          .append('circle')
            .attr('class', 'dot')
            .attr('r', 5)
            .attr('cx', (d)=> xScale(d.secondsBehind) )
            .attr('cy', (d)=> yScale(d.place) )
            .style('fill', (d)=> colorScale(d.dopingAllegations) );
    
      graph.selectAll('.label')
        .data(graphData)
        .enter()
          .append('text')
            .attr('class', 'label')
            .attr('x', (d)=> xScale(d.secondsBehind) + 10)
            .attr('y', (d)=> yScale(d.place) + 4)
            .text( (d)=> d.name );
  
    // d3.selectAll('.dot')
    //   .on('mouseover', mouseover)
    //   .on('mouseout', mouseout);
  
    var legend = svg.selectAll('.legend')
                    .data(colorScale.domain())
                    .enter()
                      .append('g')
                        .attr('class', 'legend')
                        .attr('transform', function(d,i){return 'translate(0,' +i*20+')';});

    legend.append('rect')
          .attr('x', width)
          .attr('y', 100)
          .attr('width', 18)
          .attr('height', 18)
          .style('fill', colorScale);

    legend.append('text')
          .text((d)=> d)
          .attr('x', width - 18)
          .attr('y', 108)
          .attr('dy', '.35em')
          .style('text-anchor', 'end');

};




// function mouseout(){
//     div.style('opacity', 1e-6);
// }

function formatMinSec(d){
    if( d % 60 > 9){
      return Math.floor(d/60) +':'+  d%60
    } else {
      return Math.floor(d/60) +':0'+  d%60
    }
}
body {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.dot {
  stroke: #000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.6.1/lodash.min.js"></script>

score:1

Instead of this:

svg.selectAll('.dot')               // creates the correct number of dots
    .data(graphData)
    .enter()
      .append('circle')
        .attr('class', 'dot')
        .attr('r', 5)
        .attr('cx', (d)=> xScale(d.secondsBehind) )
        .attr('cy', (d)=> yScale(d.place) )
        .style('fill', (d)=> colorScale(d.dopingAllegations) );

  svg.selectAll('.label')       // does not create the last two text elements 
    .data(graphData)
    .enter()
      .append('text')
        .attr('class', 'label')
        .attr('x', (d)=> xScale(d.secondsBehind) + 10)
        .attr('y', (d)=> yScale(d.place) + 4)
        .text( (d)=> d.name );

Do it like this:

var gs = svg.selectAll('.dot')
    .data(graphData)
    .enter();
      gs.append('circle')
        .attr('class', 'dot')
        .attr('r', 5)
        .attr('cx', (d)=> xScale(d.secondsBehind) )
        .attr('cy', (d)=> yScale(d.place) )
        .style('fill', (d)=> colorScale(d.dopingAllegations) );

  gs
      .append('text')
        .attr('class', 'label')
        .attr('x', (d)=> xScale(d.secondsBehind) + 10)
        .attr('y', (d)=> yScale(d.place) + 4)
        .text( (d)=> { return d.name; } );

working code here

Other option is that instead of

  svg.selectAll('.label')       // does not create the last two text elements 
    .data(graphData)
    .enter()
      .append('text')
        .attr('class', 'label')
        .attr('x', (d)=> xScale(d.secondsBehind) + 10)
        .attr('y', (d)=> yScale(d.place) + 4)
        .text( (d)=> d.name );

Do this:

  svg.selectAll('.label')
    .data(graphData, function(d) {
      if (d) {
        return d.place; //unique identifier of the data, otherwise Marco Pantani will come only once.
      }
    })
    .enter()
    .append('text')
    .attr('class', 'label')
    .attr('x', (d) => xScale(d.secondsBehind) + 10)
    .attr('y', (d) => yScale(d.place) + 4)
    .text((d) => d.name);

Working code here


Related Query

More Query from same tag