score:3

Accepted answer

You set the class indicating positive/negative coloring only when entering elements:

   u.enter()
    .append("rect")
    .attr("class", function (d) { return "rect rect--" + (d[selectedVar] < 0 ? "negative" : "positive") })
    .merge...

Elements are only entered once and methods chained to an enter selection called only once per entered element. If the data is updated, you aren't re-evaluating if the class needs to be switched for any already existing bar (they have already been entered, they are in the update selection). Instead, merge the update and the enter, then set the class:

u.enter()
 .append("rect")
 .merge(u)
 .attr("class", function (d) { return "rect rect--" + (d[selectedVar] < 0 ? "negative" : "positive") })
 .transition()
 ...

Now, whenever elements are entered or updated with new data, the color will also be (re-)set as needed.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>d3 Bar Chart</title>
    <script src="https://d3js.org/d3.v4.js"></script>
    <style>
        body {
            font: 10px sans-serif;
        }

        select {
            display: block;
        }

        .rect {
            opacity: 0.8;
        }

        .rect--positive {
            fill: green;
        }

        .rect--negative {
            fill: red;
        }

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

    </style>
</head>

<body>
    <!-- Add 2 buttons -->
    <button class="dataOption" onclick="update('col1');">Data Set 1</button>
    <button class="dataOption" onclick="update('col2');">Data Set 2</button>
    <button class="dataOption" onclick="update('col3');">Data Set 3</button>

    <!-- Create a div where the graph will take place -->
    <div id="my_dataviz"></div>

    <script type="text/javascript">

        // set the dimensions and margins of the graph
        var margin = { top: 30, right: 30, bottom: 70, left: 60 },
            width = 960 - margin.left - margin.right,
            height = 400 - margin.top - margin.bottom;

        var svg = d3.select("#my_dataviz")
            .append("svg")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("viewBox", "0 0 1000 500")
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")

        // Initialize the X axis
        var x = d3.scaleBand()
            .range([0, width])
            .padding(0.2);

        var xAxis = svg.append("g")
            .attr("transform", "translate(0," + height + ")")

        // Initialize the Y axis
        var y = d3.scaleLinear()
            .range([height, 0])

        var yAxis = svg.append("g")

        // A function that create / update the plot for a given variable:
        function update(selectedVar) {

            // Pull and parse the Data
            d3.csv("https://raw.githubusercontent.com/JPichichero/test/main/data", function (data) {

                // Add X axis
                x.domain(data.map(function (d) { return d.date; }))
                xAxis.transition().duration(1000).call(d3.axisBottom(x))

                // Add Y axis
                y.domain([d3.min(data, function (d) { return +d[selectedVar] }), d3.max(data, function (d) { return +d[selectedVar] })]);
                yAxis.transition().duration(1000).call(d3.axisLeft(y))

                // variable u: map data to existing bars
                var u = svg.selectAll("rect")
                    .data(data)

                // update bars
                u
                    .enter()
                    .append("rect")
                    .merge(u)
                    .attr("class", function (d) { return "rect rect--" + (d[selectedVar] < 0 ? "negative" : "positive") })
                    .transition()
                    .duration(1000)
                    .attr("x", function (d) { return x(d.date); })
                    .attr("y", function (d) { return y(Math.max(0, d[selectedVar])); })
                    .attr("width", x.bandwidth())
                    .attr("height", function (d) { return Math.abs(y(d[selectedVar]) - y(0)); })
            })
        }

        // Initialize plot
        update('col1')

    </script>
</body>

</html>

If you want to transition the color, you can't use a class to set the fill: the transition will interpolate between "negative" and "positive", two strings, but you could interpolate between two css colors if you set the fill attribute:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>d3 Bar Chart</title>
    <script src="https://d3js.org/d3.v4.js"></script>
    <style>
        body {
            font: 10px sans-serif;
        }

        select {
            display: block;
        }

        .rect {
            opacity: 0.8;
        }

        .rect--positive {
            fill: green;
        }

        .rect--negative {
            fill: red;
        }

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

    </style>
</head>

<body>
    <!-- Add 2 buttons -->
    <button class="dataOption" onclick="update('col1');">Data Set 1</button>
    <button class="dataOption" onclick="update('col2');">Data Set 2</button>
    <button class="dataOption" onclick="update('col3');">Data Set 3</button>

    <!-- Create a div where the graph will take place -->
    <div id="my_dataviz"></div>

    <script type="text/javascript">

        // set the dimensions and margins of the graph
        var margin = { top: 30, right: 30, bottom: 70, left: 60 },
            width = 960 - margin.left - margin.right,
            height = 400 - margin.top - margin.bottom;

        var svg = d3.select("#my_dataviz")
            .append("svg")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("viewBox", "0 0 1000 500")
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")

        // Initialize the X axis
        var x = d3.scaleBand()
            .range([0, width])
            .padding(0.2);

        var xAxis = svg.append("g")
            .attr("transform", "translate(0," + height + ")")

        // Initialize the Y axis
        var y = d3.scaleLinear()
            .range([height, 0])

        var yAxis = svg.append("g")

        // A function that create / update the plot for a given variable:
        function update(selectedVar) {

            // Pull and parse the Data
            d3.csv("https://raw.githubusercontent.com/JPichichero/test/main/data", function (data) {

                // Add X axis
                x.domain(data.map(function (d) { return d.date; }))
                xAxis.transition().duration(1000).call(d3.axisBottom(x))

                // Add Y axis
                y.domain([d3.min(data, function (d) { return +d[selectedVar] }), d3.max(data, function (d) { return +d[selectedVar] })]);
                yAxis.transition().duration(1000).call(d3.axisLeft(y))

                // variable u: map data to existing bars
                var u = svg.selectAll("rect")
                    .data(data)

                // update bars
                u
                    .enter()
                    .append("rect")
                    .attr("class", "rect")
                    // Set fill on enter if you want to transition to the final color:
                    .attr("fill", "black")            
                    .merge(u)
                    .transition()
                    .attr("fill", function(d) {
                        return d[selectedVar] < 0 ? "red" : "green"                  
                    })                   
                    .duration(1000)
                    .attr("x", function (d) { return x(d.date); })
                    .attr("y", function (d) { return y(Math.max(0, d[selectedVar])); })
                    .attr("width", x.bandwidth())
                    .attr("height", function (d) { return Math.abs(y(d[selectedVar]) - y(0)); })
            })
        }

        // Initialize plot
        update('col1')

    </script>
</body>

</html>

score:0

This code isn't optimized... and could be much more efficient is my hunch, but it works. I have only heard of d3 and don't have the proper experience with it to optimize it, though I may take a crack at it. Long story short, the svg was saving the data for the rects and any attributes associated with them. I just removed the svg and reinstantiated the d3 object and other attributes, etc, then appended it to the document. This is an okay solution provided you aren't dealing with 10s of 1000s of data bars... which sounds highly unlikely, but I guess I could be surprised.

// set the dimensions and margins of the graph
        var margin = { top: 30, right: 30, bottom: 70, left: 60 },
            width = 960 - margin.left - margin.right,
            height = 400 - margin.top - margin.bottom;

        var svg = d3.select("#my_dataviz")
            .append("svg")
            .attr("id", "test")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("viewBox", "0 0 1000 500")
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")

        // Initialize the X axis
        var x = d3.scaleBand()
            .range([0, width])
            .padding(0.2);

        var xAxis = svg.append("g")
            .attr("transform", "translate(0," + height + ")")

        // Initialize the Y axis
        var y = d3.scaleLinear()
            .range([height, 0])

        var yAxis = svg.append("g")

        // A function that create / update the plot for a given variable:
        function update(selectedVar) {

/*
**
** Changes were made between these lines
**
*/

        // set the dimensions and margins of the graph
        var margin = { top: 30, right: 30, bottom: 70, left: 60 },
            width = 960 - margin.left - margin.right,
            height = 400 - margin.top - margin.bottom;

        var svg = d3.select("#my_dataviz")
            .append("svg")
            .attr("id", "test")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("viewBox", "0 0 1000 500")
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")

        // Initialize the X axis
        var x = d3.scaleBand()
            .range([0, width])
            .padding(0.2);

        var xAxis = svg.append("g")
            .attr("transform", "translate(0," + height + ")")

        // Initialize the Y axis
        var y = d3.scaleLinear()
            .range([height, 0])

        var yAxis = svg.append("g")


          document.getElementById('test').remove();

/*
**
** end of changes
**
*/

            // Pull and parse the Data
            d3.csv("https://raw.githubusercontent.com/JPichichero/test/main/data", function (data) {

                // Add X axis
                x.domain(data.map(function (d) { return d.date; }))
                xAxis.transition().duration(1000).call(d3.axisBottom(x))

                // Add Y axis
                y.domain([d3.min(data, function (d) { return +d[selectedVar] }), d3.max(data, function (d) { return +d[selectedVar] })]);
                yAxis.transition().duration(1000).call(d3.axisLeft(y))

                // variable u: map data to existing bars
                var u = svg.selectAll("rect")
                    .data(data)

                // update bars
                u
                    .enter()
                    .append("rect")
                    .attr("class", function (d) { return "rect rect--" + (d[selectedVar] < 0 ? "negative" : "positive") })
                    .merge(u)
                    .transition()
                    .duration(1000)
                    .attr("x", function (d) { return x(d.date); })
                    .attr("y", function (d) { return y(Math.max(0, d[selectedVar])); })
                    .attr("width", x.bandwidth())
                    .attr("height", function (d) { return Math.abs(y(d[selectedVar]) - y(0)); })
            })
        }

        // Initialize plot
        update('col1')
        body {
            font: 10px sans-serif;
        }

        select {
            display: block;
        }

        .rect {
            opacity: 0.8;
        }

        .rect--positive {
            fill: green;
        }

        .rect--negative {
            fill: red;
        }

        .axis path,
        .axis line {
            fill: none;
            stroke: #000;
            shape-rendering: crispEdges;
        }
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>d3 Bar Chart</title>
    <script src="https://d3js.org/d3.v4.js"></script>
</head>

<body>
    <!-- Add 2 buttons -->
    <button class="dataOption" onclick="update('col1');">Data Set 1</button>
    <button class="dataOption" onclick="update('col2');">Data Set 2</button>
    <button class="dataOption" onclick="update('col3');">Data Set 3</button>

    <!-- Create a div where the graph will take place -->
    <div id="my_dataviz"></div>
    
</body>

</html>


Related Query

More Query from same tag