EN VI

Javascript - D3 update graph after data changes?

2024-03-11 23:30:05
How to Javascript - D3 update graph after data changes

I have a problem with understanding how update works. My goal is that the fill changes its value when data changes its values. After running the following code in a HTML-file I write e.g. board_state[7][9] = 1 in the console and then drawBoard(). Now it adds 10 more rect-Elements to each g instead of updating their value. How can I update the current one with the changed values without adding new ones?

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Example</title>
        <meta charset='UTF-8'>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js" integrity="sha512-M7nHCiNUOwFt6Us3r8alutZLm9qMt4s9951uo8jqO4UwJ1hziseL6O3ndFyigx6+LREfZqnhHxYjKRJ8ZQ69DQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
        <style>
            rect{
                stroke: #ffffff;
            }
            
        </style>
    </head>
    <body>
        <script>
            let board_state = [[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]];
                               
            const svg = d3.select("body").append("svg");
            let height = window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); 
            let width = height / 2;
            svg.attr("height", height).attr("width", width);
            
            let board = svg.append("g");
            let x = d3.scaleBand().range([0, width]).domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
            let y = d3.scaleBand().range([0, height]).domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
            let color = d3.scaleOrdinal()
                          .domain([0, 1])
                          .range(["#aaaaaa", "red"]);
            
            let local = d3.local();
            
            function drawBoard(){
                board.selectAll("g")
                 .data(board_state)
                 .join(
                    enter => enter.append("g"),
                    update => update,
                    exit => exit.remove()
                 )
                 .selectAll(".field")
                 .data( function(d,i){ 
                    local.set(this, i)
                    return d;
                 })
                 .join(
                    enter => enter.append("rect"),
                    update => update,
                    exit => exit.remove()
                 )
                 .attr("x", function(d, i){ return x(i); })
                 .attr("y", function(d){ return y(local.get(this)); })
                 .attr("height", y.bandwidth())
                 .attr("width", x.bandwidth())
                 .attr("fill", function(d){ return color(d); });
            }

            drawBoard();
            
        </script>
    </body>
</html>

Solution:

In your second .selectAll you are selecting by class name but never assign that class. This means your selection will be empty and all the data will be "entering" again.

Easy fix:

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Example</title>
        <meta charset='UTF-8'>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js" integrity="sha512-M7nHCiNUOwFt6Us3r8alutZLm9qMt4s9951uo8jqO4UwJ1hziseL6O3ndFyigx6+LREfZqnhHxYjKRJ8ZQ69DQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
        <style>
            rect{
                stroke: #ffffff;
            }
            
        </style>
    </head>
    <body>
        <script>
            let board_state = [[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],
                               [0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0]];
                               
            const svg = d3.select("body").append("svg");
            let height = 600 //window.innerHeight || (window.document.documentElement.clientHeight || window.document.body.clientHeight); 
            let width = height / 2;
            svg.attr("height", height).attr("width", width);
            
            let board = svg.append("g");
            let x = d3.scaleBand().range([0, width]).domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
            let y = d3.scaleBand().range([0, height]).domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
            let color = d3.scaleOrdinal()
                          .domain([0, 1])
                          .range(["#aaaaaa", "red"]);
            
            let local = d3.local();
            
            function drawBoard(){
                board.selectAll("g")
                 .data(board_state)
                 .join(
                    enter => enter.append("g"),
                    update => update,
                    exit => exit.remove()
                 )
                 .selectAll(".field")
                 .data( function(d,i){ 
                    local.set(this, i)
                    return d;
                 })
                 .join(
                    enter => enter.append("rect").attr("class", "field"),
                    update => update,
                    exit => exit.remove()
                 )
                 .attr("x", function(d, i){ return x(i); })
                 .attr("y", function(d){ return y(local.get(this)); })
                 .attr("height", y.bandwidth())
                 .attr("width", x.bandwidth())
                 .attr("fill", function(d){ return color(d); });
            }

            drawBoard();
            setTimeout(() => {
              board_state[7][9] = 1;
              drawBoard();
            }, 3000)
            
        </script>
    </body>
</html>

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login