D3 Bar Chart Problems

I’m having issues with three things here.

  • I’m doing something wrong to set the height of the bars (the problem might be with scaleFactorY in my code). The bottom of the bars goes beneath the x-axis for some reason, but I don’t understand why.
  • I can’t figure out how to get the x-axis ticks to be the right values. The d3js docs are opaque to my beginner eyes. :confused:
  • I don’t understand what the test suite wants for a tooltip. If I use append("title").attr("id","tooltip"), then I can’t figure out how to get an individual tooltip to show and all the others to hide. (I’d use attr("id", (d, i) => "tooltip" + i) but that’s not what the test suite wants ig.) If I instead use a separate <div id="tooltip"></div> explicitly declared in HTML and update it whenever the mouse moves (using a trick to grab the element that the mouse is over), the test suite doesn’t think the element is visible and invisible at the right times, even though manual testing confirms that it is.

Apologies for the novel… any ideas to help me get unstuck?

Hey there! I’m no coding expert (I just finished the 5th project in this course two days ago), but here’s where I think your problems lie:

In regards to the bottom of the bars going bneath the x-axis, this happens because the “y” attribute of the “rect” elements has been incorrectly set: it should be the same value for both the x-axis as for the bars’ “y” attribute. The value for the x-axis’s “y” attr (or rather, for the “transform-translate” attribute is correctly set to " h - paddingTopBottom". The “y” needs to also use “h - paddingTopBottom” but you also need to subtract from this the “rect” element’s height, which you’ve coded right below the “rect” element’s “y” attr, so you’ll probably be able to figure out what to do with just a quick look at that part of your code.

I regards to getting the x-axis ticks to show the correct values, keep in mind that you’re not accessing the date information when using the x-scale, as its domain works with indexes, which is why it goes from 0 to about 273 (there are about 273 objects). You could consider creating a new scale to use with the x-axis, and set this new scale’s domain to go from the date of the first object to the date of the last object (creating “new Date()” objects might be necessary here)

I regards to the tooltip thing, I hate to inform that the final projects of this unit require you to use many things which these units have not covered AT ALL. And the exercises won’t even tell you that you need to use things you don’t know even know about, much less tell you what those things are. I wasted so, so much time triyng to figure out how to do things using the tools taught in this course, only to find out hours later that it was impossible. It’s honestly been infuriating. I’m very thankful for fCC, specially since it’s free, but not giving us a heads up when something requires us to use tools we don’t know about is a major oversight. The “append.(title)” previously covered in this course is completely useless here, though I guess you probably already know that after taking a look at your code. I used a somewhat different approach which I learned from the following website:

I think it’s a pretty straightforward explanation and it certainly helped me. Heads up: the 4th and 5th projects (Choropleth and Treemap). Also require you to use tools that have not been covered in this unit. I’ll try to come back and post the links to those guides later.

As far as I’m concerned, the fact that you just got this certification means that it’s fresh on your mind, and I’m grateful for your help regardless, Rodrigo!

Bars below x-axis: Thank you for pointing me to that part of my code. I had been using the yScale() function thinking that it’d take care of everything for me, but evidently it didn’t. But then I realized I could literally just use the equation I was using to calculate the height of each bar and subtract that (using the formula form that fCC provides in “Invert SVG Elements,” h - m * d). My callback function is now (d, i) => (h - 20) - (d[1] * scaleFactorY), instead of some weirdness with yScale() lol. I think I had assumed that yScale() did the calculation for me, but I guess not. This declarative programming stuff gets on my nerves sometimes lol. I wanna see everything that my code is doing!

X-axis ticks: I figured it had to be something like that using some kind of date, but I’ll have to dig into that more tomorrow. I’ll probably post another reply on this thread when I make progress here.

Tooltips: Yeah I got that impression browsing this forum that fCC kinda left people hanging about the tooltips. Even skimming that Medium article (I’ll read it more tomorrow when I have more time to work on the project) makes a lot of sense, although it is mildly infuriating that nothing I saw mentioned using d3.select() on an id. Totally agree about fCC though, these people are absolute lifesavers, but yeah the tooltips thing feels like an oversight. I can’t find an issue for it on their GitHub, so maybe I can say something. I’ll do that tomorrow haha.

1 Like

If I’m being honest, I think the weirdness in your code was actuall that whole scaleFactorY thing in your code; I didn’t really understand what that was all about, lol. You can set the “y” and “height” attributes just using yScale. The scaleFactorY think seemed to me like an unnecessary extra step. Here’s a link to my codepen if want to check it out later:

https://codepen.io/RodrigoHLC/pen/xxmBPgd

Hola @ shy_away

En este bloque de codigo:

const yScale = d3
        .scaleLinear()
        .domain([
          d3.min(data.data, (d) => d[1]),
          d3.max(data.data, (d) => d[1])
        ])
        .range([h - paddingTopBottom, paddingTopBottom]);

El valor inicial de domain es 243.1 y debe de ser 0 , si modificas eso pasaras la prueba 11.

En este bloque de codigo:

const gx = svg
        .append("g")
        .attr("id", "x-axis")
        .attr("transform", `translate(0,${h - paddingTopBottom})`)
        .call(d3.axisBottom(xScale));

Haces referencia a xScale, sin embargo, los valores de xScale son una secuencia de numeros y los valores de data-data son fechas por lo que no coinciden.

En este caso, necesitas crear otra escala para obtener los valores de gx, la nueva escala debe ser del tipo scaleTime y en tu caso se vera asi:

    const yearsScale = d3
      .scaleTime()
      .domain([
        new Date(d3.min(data.data, (d) => d[0])),
        new Date(d3.max(data.data, (d) => d[0]))
      ])
      .range([paddingSides, w - paddingSides]);

Despues de crear la nueva escala, haces referencia a ella en gx y con eso pasaras la prueba 10.

En cuanto a las otras 2 pruebas ya no alcance a revisarlas, pero trabaja en lo que te comente y si requires mas ayuda comentalo y si puedo con mucho gusto.

Saludos

Thanks RMG and Rodrigo! I got all the tests to pass with your help.

Summary of what I did:

  • Bars below X-axis: Thanks to RMG pointing out my issue in yScale, I can actually use yScale when calculating the y and height values for each bar. Part of me still wanted to keep the “m” of h - m * d stored in scaleFactorY, but especially with Rodrigo’s prodding, I realized it was bad code since that scaling calculation isn’t used anywhere else.
  • X-axis ticks: Feels bad to copy code as I’m learning d3, but it’s much appreciated RMG! I now have an xScale, a yScale, and a separate yearsScale since I still need xScale for positioning the bars.
  • Tooltips: The Medium article Rodrigo shared was very helpful. It is a bit inaccurate though – d3’s .on() accepts a string for an event name and a callback function, into which it passes the event object, not data. The Medium article’s solution also uses a method of accessing the mouse’s x and y that broke my code (I’m assuming d3 updated something). I have this code appended on every bar now:
.on("mouseover", (event) => {
  d3.select("#tooltip")
    .style("opacity", 1)
    .attr("data-date", event.target.dataset.date)
    .text(event.target.dataset.date + ", " + event.target.dataset.gdp);
})
.on("mouseout", () => {
  d3.select("#tooltip").style("opacity", 0);
})
.on("mousemove", (event) => {
  d3.select("#tooltip").style("left", event.clientX + 20 + "px");
});

Again, thanks guys!

2 Likes