D3.js bar chart - stuck in the upside down

I am trying to get the height of my bars scaled by a linearScale I created. I also need the text for each bar to rest on top of the bar and so am using the same linearScale to situate them in the right place.

Despite the fact that I am using the same scale, my text is situated in the right spots on the graph but my bar charts are doing the exact opposite, i.e. returning the smallest value for the largest data point and v.v.

As a check I hardcoded the height with smallest and largest values in dataset. And sure enough, when I put in the smallest value I got a larger bar than when I put in the largest.

This is driving me nuts, please help.

My code so far:

<!DOCTYPE html>
<html lang = "en">
   <head>
      <script src = "https://d3js.org/d3.v4.min.js"></script>
   </head>

   <style>
      svg {
        background-color: pink;
          }

     .bar:hover {
       fill: brown;
          }

   </style>

   <body>
      <script>

      const w = 1100
      const h = 300
      const padding = 60

      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h)

        document.addEventListener('DOMContentLoaded',function(){
        document.getElementById('getMessage').onclick=function(){
          req=new XMLHttpRequest();
          req.open("GET",'https://api.worldbank.org/v2/countries/all/indicators/SP.POP.TOTL?format=json',true);
          req.send();
          req.onload=function(){
            json=JSON.parse(req.responseText);

            let yValArr = [];
            for(var i = 0; i < json[1].length; i++){
              yValArr.push(Math.round( ((json[1][i]["value"])/10000000) * 10) / 10)
            }



            document.getElementsByClassName('message')[0].innerHTML= yValArr;


            const yScale = d3.scaleLinear()
                     .domain([0, (d3.max(yValArr) + 2)])
                     .range([h-padding, padding]);


            svg.selectAll("rect")
               .data(yValArr)
               .enter()
               .append("rect")
               .attr("x", (d, i) => padding + (i * 30))
               .attr("y", (d, i) => (h - yScale(d)) - padding)
               .attr("width", 25)
               .attr("height", (d, i) => yScale(d))
               .attr("class","bar");

            svg.selectAll("text")
               .data(yValArr)
               .enter()
               .append("text")
               .attr("x", (d, i) => padding + (i * 30) + 14)
               .attr("y", (d, i) => yScale(d))
               .text((d) => d)
               .attr("font-size",5)
               .attr("fill","red")


            const yAxis = d3.axisLeft(yScale);

            svg.append("g")
               .attr("transform", "translate(" + (padding) + ", 0)")
               .call(yAxis);
          };
        };
      });

    </script>

      <br>
      <button id="getMessage">
        Get Data
      </button>


      <p class="message">
        The data will go here
      </p>
   </body>
</html>

I think you must fix the height attribute. For this reason for height more big you get a little height bar: h - YScale(d) works?

Thank @yoelvis. That fixed my height problem.

But now, I guess my y-axis coordinate calc for each rect was not right:
.attr("y", (d, i) => (h - yScale(d)) - padding)

I’ve tried different things:

.attr("y", yScale(h-(2*padding)))

and

.attr("y", padding + yScale(h-(2*padding)))

Believe it or not, there was some kind of logic behind each of those. But I have to admit, I think I am completely lost.

I’m getting hung up on when I have to shift things by the linearScale and when I don’t. I had assumed that once I get into the bar chart area of SVG, I have to move according to linearScale…but clearly I’m not right.

This is what my code right looks like now:

<!DOCTYPE html>
<html lang = "en">
   <head>
      <script src = "https://d3js.org/d3.v4.min.js"></script>
   </head>

   <style>
      svg {
        background-color: pink;
          }

     .bar:hover {
       fill: brown;
          }

   </style>

   <body>
      <script>

      const w = 1100
      const h = 300
      const padding = 60

      const svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h)

        document.addEventListener('DOMContentLoaded',function(){
        document.getElementById('getMessage').onclick=function(){
          req=new XMLHttpRequest();
          req.open("GET",'https://api.worldbank.org/v2/countries/all/indicators/SP.POP.TOTL?format=json',true);
          req.send();
          req.onload=function(){
            json=JSON.parse(req.responseText);

            let yValArr = [];
            for(var i = 0; i < json[1].length; i++){
              yValArr.push(Math.round( ((json[1][i]["value"])/10000000) * 10) / 10)
            }



            document.getElementsByClassName('message')[0].innerHTML= yValArr;


            const yScale = d3.scaleLinear()
                     .domain([0, (d3.max(yValArr) + 2)])
                     .range([h-padding, padding]);


            svg.selectAll("rect")
               .data(yValArr)
               .enter()
               .append("rect")
               .attr("x", (d, i) => padding + (i * 30))
               // .attr("y", (d, i) => (h - yScale(d)) - padding)
               // .attr("y", padding + yScale(h-(2*padding)))
               .attr("y", h-270)
               .attr("width", 25)
               .attr("height", (d, i) => h - yScale(d))
               .attr("class","bar");

            svg.selectAll("text")
               .data(yValArr)
               .enter()
               .append("text")
               .attr("x", (d, i) => padding + (i * 30) + 14)
               .attr("y", (d, i) => yScale(d))
               .text((d) => d)
               .attr("font-size",5)
               .attr("fill","red")


            const yAxis = d3.axisLeft(yScale);

            svg.append("g")
               .attr("transform", "translate(" + (padding) + ", 0)")
               .call(yAxis);
          };
        };
      });

    </script>

      <br>
      <button id="getMessage">
        Get Data
      </button>


      <p class="message">
        The data will go here
      </p>
   </body>
</html>

@sabbyiqbal Your point of view is interesting. I have not stopped to analyze the vertical axes directed downwards but to focus on my code working but we may someday understand its logic.

On the other hand, if you exchange the scale points in your scalelinear, you do not need to subtract the height of the svg from the coordinate y. :man_shrugging:

So sorry…I am not sure I understand. Can you explain a little further what you mean?

Forgive my bad English. I mean we share the same question: What is the logic of the y-axis inverted?

I told you that we should not stop for a long time in questions to which we will later get the answer.

And finally, I mean that the code below also works:

const yScale = d3.scaleLinear()
                     .domain([(d3.max(yValArr) + 2)], 0) // I modified the limits
                     .range([h-padding, padding]);

So

.attr("height", (d, i) =>  yScale(d)) // I do not use the h variable

Thanks @yoelvis.

As far as the inverted y-axis, the reason is that the SVG canvas has the origin point (x = 0, y = 0) in the top left corner and then when you want to move an element down, you have to ADD to y-coordinate value. And if you want to move the element to the left, you have to ADD x-coordinate value. So the result is that the y-axis is inverted (compared to the normal x-y axes we all learned about in math class). I guess they had to do this in order to have a standard (0,0) origin point on every SVG canvas.

Also I’m afraid that the code you proposed did not work. But thanks!!

I think I’m gonna go over some other tutorials to better understand d3.js. Will come back to these challenges later. :frowning_face::slight_smile: