Skip to content Skip to sidebar Skip to footer

Why Are The First Two Text Labels Of My Bar Chart Not Shown?

I have implemented a bar chart which is basically working fine. However, when putting labels on the bars I noticed that the labels for the first two bars are not shown above their

Solution 1:

The following section appending the labels to the bars is not working as you might have expected:

g.selectAll("text")       
  .data(dataset)       
  .enter()       
  .append("text")  

In fact, you have previously appended <text> elements to the g element, namely, the labels for your axes. Above selection will select those texts as the first two elements. When binding your data those elements are already existing and are thus not part of the enter selection. In fact, if you use your browser's developer tools to inspect e.g. the Month label you will find a __data__ property on that element indicating that the data was bound to the existing text element which is clearly not what you want.

There are basically two ways around that:

  1. Append the labels of the axes to another group. This creates distinct DOM subtrees for the axes and the bars and labels which might be a good thing in terms of separation. Selecting texts in one subtree will find texts contained in another subtree.

  2. Use a more specific selector string when creating the selection for the data join. Alternatively, use g.selectAll(null) as there are no labels on the bars, yet. Have a look at Selecting null: what is the reason behind 'selectAll(null)' in D3.js? for an explanation why this is the preferred way.

Each approach is capable of solving your problem on its own. Nonetheless, you might want to consider using a combined solution incorporating both approaches as this will make your life easier since it creates much cleaner Code for your SVG.

Side note: This works well for the <rect> elements representing the bars as there do not exist any rectangles before doing that selection. Still, the preferrable and most idiomatic way to select elements you are sure are not existing will be to use d3.selectAll(null).

The following demo shows how to implement 2. to make it work:

const dataset=[
  {
    "Month": "2013_09",
    "Count": 1
  },
  {
    "Month": "2013_10",
    "Count": 291
  },
  {
    "Month": "2013_11",
    "Count": 763
  },
  {
    "Month": "2013_12",
    "Count": 853
  }
];
	dataset.forEach(d=>d.Count=+d.Count);
	let margin={top:10,right:10,bottom:90,left:40}; 
	let width= 600-margin.left-margin.right;
	let height= 400-margin.top-margin.bottom;
	
	const svg = d3.select(".div1")                 
	.append("svg")  
	.attr("width", width+margin.left+margin.right)       
	.attr("height", height+margin.top+margin.bottom)   
	.attr("style", "outline: thin solid red;") 
	
	let g=svg.append("g")
		.attr("transform","translate(" +margin.left+ ", " +margin.top+ ")");

	g.append("text")
	  	.attr("class","x label")
	  	.attr("x", width/2)
	  	.attr("y", height+76)
	  	.attr("font-size","20px")
	  	.attr("text-anchor","middle")
	  	.text("Month")

	 g.append("text")
	  	.attr("class","y label")
	  	.attr("x", -height/2+50)
	  	.attr("y", -40)
	  	.attr("font-size","20px")
	  	.attr("text-anchor","end")
	  	.text("")
	  	.attr("transform","rotate(-90)")
	  
	let x = d3.scaleBand() 
				.domain(dataset.map(d=>d.Month)) 
            .range([0, width]) 
				.paddingInner(0.3)
				.paddingOuter(1)
	  
	
	let y = d3.scaleLinear() 
            .domain([0, d3.max(dataset,d=>d.Count)+80]) 
            .range([height, 0]);
	 
	g.selectAll(null)       
      .data(dataset)       
      .enter()       
      .append("text")  
	   .attr("font-size","11px")
	  	.attr("text-anchor","middle")
      .text((d) => d.Count)
		.attr("y",d=>y(d.Count)-3)
	   .attr("x",(d)=>x(d.Month)+4);
	 
	 
	let leftAxis=d3.axisLeft(y)
		.ticks(8)
		.tickFormat(d=>d);
	g.append("g")
			.attr("class","left axis")
	  	  	.attr("transform","translate(0,0)")
			.call(leftAxis)
	 
	let rects=g.selectAll(null)       
		.data(dataset)      
		.enter()     
		.append("rect") 
		.attr("y",d=>y(d.Count)) 
		.attr("x",d=>x(d.Month))
		.attr("width",x.bandwidth) 
		.attr("height",d=>height-y(d.Count)) 
		.attr("fill","blue") 

	let bottomAxis=d3.axisBottom(x);
	  g.append("g")
	  		.attr("class", "x axis")
	  		.attr("transform","translate(0,"+height+")")
	  		.call(bottomAxis)
	  		.selectAll("text")
	  		.attr("y","10")
	  		.attr("x","-5")
	  		.attr("text-anchor","end")
	  		.attr("transform","rotate(-40)");

 
<script src="https://d3js.org/d3.v5.js"></script>

<div class="div1" ></div>

Post a Comment for "Why Are The First Two Text Labels Of My Bar Chart Not Shown?"