Updating an array of data in D3

Seeking guidance on implementing the general update pattern in D3...

My goal is to create a basic bar chart displaying data from an array, with an input form to add new data and dynamically update the chart.

Struggling with repetition while trying to refactor the process of adding rectangles to the svg...

If anyone can explain how to add a new object to the data array and refresh the svg, that would be greatly appreciated. I hope this question complies with moderator guidelines...

Current code snippet:

app.js:

// defining width and height for the svg

var height = 500;
var width = 1000;
var barPadding = 10;
var barWidth = width / data.length - barPadding;

var maxPoints = d3.max(data, function(d){
    return d.score;
});

var myScale = d3.scaleLinear()
    .domain([0, maxPoints])
    .range([height, 0]);


var svg = d3.select('svg')
    .attr("height", height)
    .attr("width", width)
    .style("display", "block")
    .style("margin", "100px auto")
    .selectAll("rect")
    .data(data)
    .enter()
    .append("rect") 
        .attr("width", barWidth) 
        .attr("height", function(d){
            return height - myScale(d.score);
        })
        .attr("x", function(d, i){
            return (barWidth + barPadding) * i;
        })
        .attr("y", function(d, i){
            return myScale(d.score);
        })

        .attr("fill", "green");

// INPUT NEW DATA

var nameInput = "input[name='name']";
var scoreInput = "input[name='score']";

function addRect(){
    barWidth = width / data.length - barPadding;
    svg
    .append("rect") 
        .attr("height", function(d){
            return height - myScale(d.score);
        })

        .attr("x", function(d, i){
            return (barWidth + barPadding) * i;
        })

        .attr("y", function(d, i){
            return myScale(d.score);
        })

        .attr("fill", "green");
};


d3.select("form")
    .on("submit", function() {
        d3.event.preventDefault();
        var firstInput = d3.select(nameInput)
            .property("value");
        var secondInput = d3.select(scoreInput)
            .property("value");
        data.push({player: firstInput, score: secondInput });
        console.log(data);

        svg
            .data(data)
            .enter();
            addRect();
    });

html:

<body>
    <div class="display">
        <svg 
        version="1.1"
        baseProfile="full"
        xmlns="http://www.w3.org/2000/svg"
        id="letters">
    </svg>
</div>
<div class="form">
    <form action="">
        <input type="text" placeholder="Name" name="name">
        <input type="text" placeholder="Score" name="score">
        <input type="submit">
    </form>
</div>


<script src="https://d3js.org/d3.v4.js"></script>
<script src="data.js"></script>
<script src="app.js"></script>
</body>

data.js:

var data = [
    {player: "Raph", score: 12},
    {player: "Henry", score: 43},
    {player: "James", score: 29},
    {player: "Andrew", score: 200},
    {player: "Ella", score: 87},
    {player: "Bob", score: 3},
    {player: "Lil", score: 19},
    {player: "Jenny", score: 223},
    {player: "Dad", score: 33},
    {player: "Rhys", score: 45}

];

Appreciate any assistance provided,

Raph

Answer №1

It's important to update the attributes of existing rectangles when adding a new one.

Here is an example:

var data = [
    {player: "Raph", score: 12},
    {player: "Henry", score: 43},
    {player: "James", score: 29},
    {player: "Andrew", score: 200},
    {player: "Ella", score: 87},
    {player: "Bob", score: 3},
    {player: "Lil", score: 19},
    {player: "Jenny", score: 223},
    {player: "Dad", score: 33},
    {player: "Rhys", score: 45}
];
// set dimensions for the svg
var height = 200;
var width = 400;
var barPadding = 10;
var barWidth = width / data.length - barPadding;

var maxPoints = d3.max(data, function(d) {
  return d.score;
});

var myScale = d3.scaleLinear()
  .domain([0, maxPoints])
  .range([height, 0]);

var svg = d3.select("svg")
  .attr("height", height)
  .attr("width", width)
  .style("display", "block")
  .style("margin", "10px 0px"); //adjusted for appearance

drawRect(); //Creating rects

//Organized code for creating and styling rectangles into a function.
function drawRect() {
  var rect = svg
    .selectAll("rect")
    .data(data)
    .enter()
    .append("rect");

  svg
    .selectAll("rect")
    .attr("width", barWidth)
    .attr("height", function(d) {
      return height - myScale(d.score);
    })
    .attr("x", function(d, i) {
      return (barWidth + barPadding) * i;
    })
    .attr("y", function(d, i) {
      return myScale(d.score);
    })
    .attr("fill", "green");
}

// ADD NEW DATA
var nameInput = "input[name='name']";
var scoreInput = "input[name='score']";

d3.select("form")
  .on("submit", function() {
    d3.event.preventDefault();
    var firstInput = d3.select(nameInput)
      .property("value");
    var secondInput = d3.select(scoreInput)
      .property("value");
    data.push({
      player: firstInput,
      score: secondInput
    });
    barWidth = width / data.length - barPadding;
    drawRect(); //Adding new rects and updating others
  });
<script src="https://d3js.org/d3.v4.js"></script>

<body>
  <div class="display">
    <svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" id="letters">
    </svg>
  </div>
  <div class="form">
    <form action="">
      <input type="text" placeholder="Name" name="name">
      <input type="text" placeholder="Score" name="score">
      <input type="submit">
    </form>
  </div>

</body>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

finding the initial instance of a value surpassing a certain threshold in a numpy array

Consider a 2D array like the following: r1= np.array([[1,2,3,4],[2,3,4,5],[3,4,5,6]]) The task at hand is to identify, for each row, the first instance of a value greater than a predefined threshold. This can be achieved using the code snippet below: de ...

Combining the contents of two JSON Arrays to form a JSONObject in Java

My JSON Arrays are structured like this: [6, 7, 8, 9, 10, 11] [122402538, 12240345, 122496, 122617, 1227473, 1228495] The goal is to merge each Long value with another while preserving its index, resulting in a structure like: [{"id": 6, "timestamp" ...

Sorry, we couldn't locate the API route you are looking for

Within my Next.js project resides the file main/app/api/worker-callback/route.ts: import { NextApiResponse } from "next"; import { NextResponse } from "next/server"; type ResponseData = { error?: string }; export async function PO ...

What is the method for applying a Redux statement?

Experience with Redux toolkits: https://i.sstatic.net/cwu8U.png I've encountered an issue while working with Redux toolkits where I'm unable to access certain statements. I attempted the following code snippet, but it resulted in an error. c ...

How can you efficiently pass multiple JSON files as arguments for Observable Arrays, especially when the values from one file are required for use in another file?

My goal is to utilize $.getJSON to retrieve data from two JSON files and assign their values to observableArrays. Currently, I have hard-coded the JSON data into the observableArray. You can view an example on this JSFiddle link. This was my initial appro ...

Leveraging the power of node pkg to generate standalone executables while configuring npm

I have successfully used pkg to create an executable file for my node js application. Everything is working fine in that aspect. However, I am also utilizing the config module to load yaml configuration files based on the environment. During the packaging ...

Fluidly insert or delete elements

Is there a way to retrieve deleted elements from the DOM after using the jquery .remove function? I have a scenario where I am removing elements from the DOM, but now I'm wondering if it's possible to bring back those deleted elements without hav ...

sidebar that appears upon the initial page load

I'm currently working on implementing a sidebar navigation panel for my website using JavaScript, HTML, and CSS. However, I am facing an issue where the sidebar automatically opens when the page is first loaded, even before clicking on the icon to ope ...

What is the best way to create a list using only distinct elements from an array?

If I have a collection of different colors: Red Blue Blue Green I aim to extract only the unique colors and store them in an array. Subsequently, I plan to incorporate each color from that array into an existing color list. The desired outcome would l ...

Keying objects based on the values of an array

Given the array: const arr = ['foo', 'bar', 'bax']; I am looking to create an object using the array entries: const obj = { foo: true, bar: true, bax: false, fax: true, // TypeScript should display an error here becau ...

How to Query MongoDB and reference an object's properties

I'm trying to execute a script on my MongoDB that will set teacher_ids[] = [document.owner_id]. The field owner_id already exists in all the objects in the collection. Here is my current attempt: db.getCollection('readings').update({ $where ...

"Enhance Your Website with Javascript: Combining and Incorpor

I'm struggling to assign the selected attribute to the option value that is already rendered within the object. However, despite the values being equal, the selected attribute is not being added. Could this issue be related to the appending process? ...

What is the reason for the neglect of this function's definition?

Is there a reason behind the error message I am receiving? TypeError: getStatusCode(...) is not a function This error occurs when I execute the following code: const getStatusCode = require('./getStatusCode') tmpStatus = await getStatusCode({url ...

Sending empty parameter data via Ajax to an MVC controller

Initially, I had no issues passing a single parameter to my MVC Controller through Ajax. However, when I attempted to add an extra parameter, both parameters stopped sending data to the Controller. Can anyone help with this issue? Thank you! Ajax Code: ...

Triggering a Bootstrap 5 dropdown through code is only effective within the browser's developer console, rather than standard JavaScript implementation

I attempted to display a Bootstrap5 dropdown by clicking on a link in my web application. Despite trying various methods, such as dispatching events and utilizing the bootstrap Dropdown classes, I was unable to achieve success. Interestingly, both approach ...

Please do not exceed two words in the input field

I need to restrict the input field to only allow up to two words to be entered. It's not about the number of characters, but rather the number of words. Can this restriction be achieved using jQuery Validation? If not, is there a way to implement it u ...

Encountering an error message of "Cannot POST" while trying to send data through a REST client application

Upon my attempt to add a new entry into the Doctors database I created, I encountered an error that has left me perplexed. This is how my server.js file appears: const express = require('express'); const bodyParser = require('body-parser&a ...

What sets apart calling an async function from within another async function? Are there any distinctions between the two methods?

Consider a scenario where I have a generic function designed to perform an upsert operation in a realmjs database: export const doAddLocalObject = async <T>( name: string, data: T ) => { // The client must provide the id if (!data._id) thr ...

I'm experiencing an issue with uploading an image to Firebase as I continue to encounter an error message stating "task.on is not a function."

The console displays a message confirming a successful upload. const sendPost = () => { const id = uuid(); const storage = getStorage(); const storageRef = ref(storage, `posts/${id}`) const uploadTask = uploadString(storageRe ...

Stop HTML <dialog> from automatically closing using Vue

I'm working on a project where I need to use Vue to programmatically prevent an HTML dialog element from closing when the close event is triggered. Here's the code snippet I am currently using: import {ref} from 'vue'; const dialogTe ...