D3 challenge issue

Any advice about my code for the choropleth?
3 issues:

  • it says legend has not got at least 4 different fill colors.
  • im missing data from one county (which causes 2 fails)
    if I don’t append the g for the axis of the legend, it only comes back with 1 issue( the one about the fill colors ).
    I’ve been staring at it for so long now, that my brain has shut down… so any help is greatly appreciated.

Update: I have resolved the issues with the missing data. I appended the legend data to the svg and not the container. Now its working, apart from the 4 different fill colors.

<!DOCTYPE html>

<html lang="en">


    <meta charset="UTF-8">

    <meta http-equiv="X-UA-Compatible" content="IE=edge">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">


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

    <script src="https://cdn.jsdelivr.net/npm/d3-scale@4"></script>

    <script src="https://cdn.jsdelivr.net/npm/d3-axis@3"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-geo/0.0.1/d3-geo.js"></script>


        body, html {

            display: flex;

            justify-content: center;



        .svg-container {

            background-color: beige;


        #title {

            font-size: 2.5em;


        #description {

            font-size: 1.1em;



        #tooltip {

            position: absolute;


            color: whitesmoke;

            opacity: 0;

            border: 0px;

            border-radius: 6px;

            width: fit-content;

            height: fit-content;

            font-size: 0.9em;

            padding: 5px 10px;



        .legendRect {

            width: 40px;

            height: 20px;






        'use strict'

        async function getData() {

            //retrieving data

            const educationData = await d3.json('https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json')

            const countyData = await d3.json('https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json')


            //back to geojson

            const counties = topojson.feature(countyData, countyData.objects.counties)

            const states = topojson.feature(countyData, countyData.objects.states, (a, b) => a !== b)

            const nation = topojson.feature(countyData, countyData.objects.nation)

            const minMax = d3.extent(educationData, data => data.bachelorsOrHigher)


            //standard svg settings

            const margin = {top: 100, right: 30, bottom: 60, left: 100}

            const w = 1200 - margin.left - margin.right

            const h = 1000 - margin.top - margin.bottom

            const colors = ['#A3E0F0', '#99CEEF', '#8FBBED', '#85A9EC', '#7B96EA']

            const toolTip = d3.select('body')


                .attr('id', 'tooltip')

            const container = d3.select('body')


                .attr('class', 'svg-container')

                .attr('width', w + margin.left + margin.right)

                .attr('height', h + margin.top + margin.bottom)



                .text('United States Educational Attainment')

                .attr('id', 'title')

                .attr('transform', `translate(${(w + margin.left + margin.right) / 2}, ${margin.top / 2})`)

                .attr('text-anchor', 'middle')


                .text("Percentage of adults age 25 and older with a bachelor's degree or higher (2010-2014)")

                .attr('id', 'description')

                .attr('transform', `translate(${(w + margin.left + margin.right) / 2}, ${margin.top / 2 + 40})`)

                .attr('text-anchor', 'middle')

            const svg = container.append('svg')

                .attr('class', 'svg')

                .attr('width', w)

                .attr('height', h)

                .attr('x', margin.left)

                .attr('y', margin.top)

            //creating legend based on colors array

            const colorScale = d3.scaleLinear()


                .range([0, 200])

            const xAxis = d3.axisBottom(colorScale)







                .attr('id', 'legend')

                .attr('class', 'legendRect')

                .attr('fill', (d, i) => colors[i])

                .attr('x', (d, i) => 0 + 40*i)

                .attr('y', h - margin.bottom)



                .attr('transform', `translate(0, ${h - margin.bottom + 20})`)


                .text('% of adults aged 25 and over')

                .attr('x', 0)

                .attr('y', `${h - margin.bottom - 20}`)                

                .style('font-size', '0.8em')

            //creating and rendering geopath

            const path = d3.geoPath()





                .attr('d', path)

                .attr('transform', `scale(1.1, 1.1)`)

                .attr('class', 'county')

                .attr('data-fips', d => d.id)

                .attr('data-education', d => educationData.find(x => x.fips === d.id).bachelorsOrHigher)

                .attr('fill', d => chooseColor(d.id))

                .on('mouseover', function(e) {

                    const county = d3.select(this)

                    county.attr('fill', 'black')

                    toolTip.style('opacity', 0.9)

                        .attr('data-education', e.target.attributes[4].value)

                        .style('left', `${e.clientX + 40}px`)

                        .style('top', `${e.clientY}px`)

                    toolTip.html(`${educationData.find(x => x.fips === parseInt(e.target.attributes[3].value)).area_name} in ${educationData.find(x => x.fips === parseInt(e.target.attributes[3].value)).state}: ${e.target.attributes[4].value}`)


                .on('mouseout', function(e) {

                    const county = d3.select(this)

                    toolTip.style('opacity', 0)

                    county.attr('fill', chooseColor(parseInt(this.attributes[3].value)))



            //creating and rendering additional states and nation paths



                .attr('fill', 'none')

                .attr('stroke', 'gray')

                .attr('transform', `scale(1.1, 1.1)`)

                .attr('d', path)



                .attr('fill', 'none')

                .attr('stroke', 'darkgray')

                .attr('transform', `scale(1.1, 1.1)`)

                .attr('d', path)

            function chooseColor(d) {

                if (educationData.find(x => x.fips === d).bachelorsOrHigher < 12.5) {

                        return colors[0]


                    if (educationData.find(x => x.fips === d).bachelorsOrHigher >= 12.5 && educationData.find(x => x.fips === d).bachelorsOrHigher < 25) {

                        return colors[1]


                    if (educationData.find(x => x.fips === d).bachelorsOrHigher >= 25 && educationData.find(x => x.fips === d).bachelorsOrHigher < 37.5) {

                        return colors[2]


                    if (educationData.find(x => x.fips === d).bachelorsOrHigher >= 37.5 && educationData.find(x => x.fips === d).bachelorsOrHigher < 50) {

                        return colors[3]


                    if (educationData.find(x => x.fips === d).bachelorsOrHigher >= 50) {

                        return colors[4]






    <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>




I think the test can’t find your legend. You have a g with the legend axis attached to the svg and you attach the legend rectangles to the the svg as well (plus, they all have the same id, which is bad). I believe the tests are looking for an element with id="legend" that actually contains the legend rectangles (looking for children rects) with different fill colors. One fix would be to redo the g with the legend axis, give it the id="legend" and add the legend rectangles to it instead of the svg.

This posted code still has the missing Marion County, Arkansas, which is the first county in the set. You could redo

      .attr('data-fips', d => d.id)


      .attr('data-fips', d => {
        return d.id;

or try some similar logging in that area to see why the first county is being swallowed.

thanks. I noticed that I had not made a g for the legend , but just append the rectangles to the svg. I now have all the legend items in a g and it accepts it.