Design a button in d3.js that toggles the visibility of a rectangle and text

Looking to use d3.js to create a simple list from data in a box, complete with a button to toggle the visibility of the box and its content. Ran into two errors during implementation:

1) The list is starting at item 2 for some reason - where's the first one?

2) Clicking the button to hide the box doesn't hide the content, just the box itself.

Any suggestions on how to fix this?

jsFiddle: here

new Vue({
  el: "#app",
  data: {
    todos: [
      { text: "Learn JavaScript", done: false },
      { text: "Learn Vue", done: false },
      { text: "Play around in JSFiddle", done: true },
      { text: "Build something awesome", done: true }
    ]
  },
  
  mounted : function() {
    this.start();
  },
  
  methods: {
    start : function(){
    
      var margin = {top: 10, right: 10, bottom: 30, left: 10},
          width = 960,
          height = 500;
      var active = true;
      
      var svg = d3.select("body").append("svg")
                  .attr("width", width)
                  .attr("height", height);
      
      var box = svg.append("rect")
                      .attr("x", 20)
                      .attr("y", 20)
                      .attr("rx", 20)
                      .attr("ry", 20)
                      .attr("id", "list")
                      .attr("height", 400)
                      .attr("width", 500)
                      .style("stroke", "#112233")
                      .style("fill", "#DDD")
                      .style("stroke-width", 1);
                      
      var items = svg.selectAll("rect")
                      .append("g")
                      .attr("id", "texts")
                      .data(this.todos)
                      .enter()
                      .append("text")
                      .style("fill", "#f00")
                      .attr("x", 50)
                      .attr("y", function (d, i) {
                        return 80 + (i) * 20;
                      })
                      .text(function (d, i) {
                        return (1 + i) + ") " + d.text;
                      });

     svg.append("circle")
          .attr("cx", 20 + 500 - 10)
          .attr("cy", 20 + 10)
          .attr("r", 20)
          .attr("id", "closer")
          .style("stroke", "#fdf8fe")
          .style("fill", "red")
          .style("stroke-width", 1)
          .on("click", function (d, a, i) {
            active = (active) ? false : true,
            newOpacity = active ? 1 : 0;
            console.log(newOpacity)
            d3.select("#list").style("opacity", newOpacity);
            d3.select("#texts").style("opacity", newOpacity);
              });
                  
    },
  
    toggle: function(todo){
      todo.done = !todo.done
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h2>Todos:</h2>

<div id="boxList"></div>

</div>

Answer №1

Here is where the issues stem from:

   var items = svg.selectAll("rect")
        .append("g")
        .attr("id", "texts")
        .data(this.todos)
        .enter()
        .append("text")

The problem arises because a rectangle is already appended, so when we select that rectangle and append a g to it with an id of texts, this results in invalid SVG code. To resolve this and achieve our goal of having all the text elements inside a g, we can restructure the code as follows:

 svg.append("g")           // create a `g`
    .attr("id","texts")
    .selectAll("text")     // select all the `text` elements in the new `g`
    .data(this.todos)
    .enter()
    .append("text")      // append child `text` elements to the new `g`
    ...

This restructuring also fixes the issue of starting the list at item 2. The function selection.enter() generates an SVG/HTML element for each item in the data array where a corresponding element doesn't already exist. Since we selected an existing rect with selectAll("rect"), the first item in the data array is bound to that rectangle. As there's no need to add an element for the first item since one already exists, selection.enter() doesn't create an element for the first item.

If you want to generate an empty selection to include everything, you can use selectAll(null)

new Vue({
  el: "#app",
  data: {
    todos: [
      { text: "Learn JavaScript", done: false },
      { text: "Learn Vue", done: false },
      { text: "Play around in JSFiddle", done: true },
      { text: "Build something awesome", done: true }
    ]
  },
  
  mounted : function() {
    this.start();
  },
  
  methods: {
    start : function(){
      
      var margin = {top: 10, right: 10, bottom: 30, left: 10},
      width = 960 ,
      height = 500 ;
      var active = true;
      
      var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height );
      
      var box = svg.append("rect")
            .attr("x", 20)
            .attr("y", 20 )
            .attr("rx", 20)
            .attr("ry", 20)
            .attr("id", "list")
            .attr("height",400)
            .attr("width", 500)
            .style("stroke", "#112233")
            .style("fill", "#DDD")
            .style("stroke-width", 1);
                    
          var items = svg.append("g")   
         .attr("id","texts")
        .selectAll("text")   
        .data(this.todos)
        .enter()
        .append("text")    
            .style("fill", "#f00")
            .attr("x",  50)
            .attr("y", function (d, i) {
             return 80+(i) * 20;
            })
            .text(function (d, i) {
            return (1 + i) + ") " + d.text;
            });

       svg.append("circle")
            .attr("cx", 20+500-10)
            .attr("cy", 20+10)
            .attr("r", 20)
            .attr("id", "closer")
            .style("stroke", "#fdf8fe")
            .style("fill", "red")
            .style("stroke-width", 1)
            .on("click", function (d, a, i) {
                active   = (active) ? false : true,
                newOpacity = active ? 1 : 0;
                console.log(newOpacity)
                d3.select("#list").style("opacity", newOpacity);
                d3.select("#texts").style("opacity", newOpacity);
              });
                    
    
    },
  
    toggle: function(todo){
      todo.done = !todo.done
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <h2>Todos:</h2>

<div id="boxList"></div>

</div>

Answer №2

  1. replace "rect" with "text" for the first item

    var items = svg .selectAll("text") // don't use rect

  2. add the following line inside the circle's click event

    i[0].parentNode.innerHTML = ""

      var items = svg .selectAll("text")
            .data(this.todos)
            .enter()
            .append("text")
            .style("fill", "#123")
            .attr("x",  50)
            .attr("y", function (d, i) {
                return 80 + (i) * 20;
             })
             .text(function (d, i) {
                  return (1 + i) + ") " + d.text;
             });
    
      svg.append("circle")
            .attr("cx", 20+500-10)
            .attr("cy", 20+10)
            .attr("r", 20)
            .attr("id", "closer")
            .style("stroke", "#fdf8fe")
            .style("fill", "red")
            .style("stroke-width", 1)
            .on("click", function (d, a, i) {
                   i[0].parentNode.innerHTML = ""
    
                   var active   = (active) ? false : true,
                   newOpacity = active ? 0 : 1;
                    console.log(newOpacity)
                    d3.select("#list").style("opacity", newOpacity);                    
                });
    

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

What strategies can I use to eliminate nested loops while constructing a navigation?

I am working with a JSON file that contains data for a navigation panel consisting of links. A brief snippet of the file is shown below: [ { "category": "Pages", "links": [ { "url": "#", "cap ...

Shorten a value within an array of objects using JavaScript

How can I truncate a value in an array of objects based on specific criteria? Let's use the following example: var items = [ { name: "CN=arun, hjsdhjashdj,jsdhjsa,kshd", status: "Present" }, { name: "CN=manohar, abcdefghij,111111,2222222", s ...

I am in the process of transforming my basic JS Array into one that contains key/value

Currently, I am utilizing jQuery to create an Array in the following manner: var arr = new Array(); $('#some-form .some-input').each(function() { arr.push($(this).val()); ...

The compatibility between Javascript and AJAX functions is currently not functioning as expected

I am attempting to send some data to the server using AJAX with the value obtained from a JavaScript variable. Here is the code snippet: <script type="text/javascript> var url; function applyPhoto(_src) { url = _src; var pho ...

Switch navigation - always display the menu on the existing page

I have a toggle menu implemented. Please check out the code and functionality on JsFiddle When clicking on a h3 tag like Category 1 (which is an a tag), the menu opens and remains open on the current page. However, if you click on the h3 tag (Category1) ...

Do not allow nested objects to be returned

I am facing an issue with typeorm, where I have a queryBuilder set up like this: const projects = await this.conn.getRepository(UserProjectRelations).createQueryBuilder("userProject") .innerJoin("userProject.userId", ...

Can anyone help me figure out the best way to test for observable errors in Angular2?

I'm currently working on creating unit test cases for my Angular2 components. Up until now, the test cases have been running smoothly. However, I've run into some issues with my asynchronous calls. For example, I have a method for creating a n ...

Encountering npm install failure post updating node version

When attempting to execute npm i, the following error message is now appearing: npm i npm ERR! path /home/ole/.npm/_cacache/index-v5/37/b4 npm ERR! code EACCES npm ERR! errno -13 npm ERR! syscall mkdir npm ERR! Error: EACCES: permi ...

Enhancing Bootstrap with VueJS for better component ordering

I've been struggling with Vue components in Bootstrap lately. I am attempting to create collapsible Bootstrap content in Vue, and here is the current code: HTML <div class="col-sm main-content" id="main-content"> <p&g ...

Breaking Down the Process of Exporting a Next.js Static Website into Manageable Parts

I am facing memory issues while building my website using Next.js's Static HTML Export. The site has a large number of static pages, approximately 10 million. Is it feasible to export these pages in batches, like exporting 100k pages in each build ite ...

Vue: event triggers malfunctioning and components unresponsive

I am new to Vue.js and I'm attempting to trigger an event from my grand-child component (card) to the child component (hand) and then to the parent component (main): card (emit play event) => hand (listen for play event and emit card-play event) ...

If the checkbox is selected, include an anonymous email in the field and conceal the field

I am looking to enhance my Feedback form by providing users with the option to submit feedback anonymously. However, my CMS requires both Email and Name fields to be mandatory. To address this, I am considering adding a checkbox that, when selected, auto ...

What is the best way to eliminate a particular element from an array produced using the .map() function in

I am experiencing an issue with my EventCell.tsx component. When a user clicks on the component, an event is created by adding an element to the components state. Subsequently, a list of Event.tsx components is rendered using the .map() method. The problem ...

The size of the Webpack bundle grows with each subsequent build

For my project, I am utilizing webpack to package it as a library. The project consists of a components library, and for each component residing in its own directory under src/ui, I am creating small bundles. Here is an example component structure: src/ ...

Firing a custom jQuery event when a page loaded via AJAX is complete, ensuring it is triggered

I am facing an issue with a particular scenario. There is a page that contains a script to load a child page and log a custom event, which is triggered in a Subform. --Index.html-- <body> <input type="button" class="clickable" value="Load child ...

Is it possible to halt the set timeout function in an AJAX call once a specific condition has been satisfied?

I have the following code snippet that is currently functioning correctly, but I am looking to implement a way to disable the automatic refreshing once a specific condition is satisfied. enter code here $(document).ready(function() { ...

Consistently encountering issues when attempting to submit JSON data via POST request (body in raw format

I'm facing an issue with sending data to my server. Currently, I am working on a project using react native and axios version ^0.16.2. let input = { 'longitude': -6.3922782, 'latitude': 106.8268856, 'content': &apos ...

Assign the value of one dropdown menu based on the selected value of another dropdown menu using PHP

When a selection is made in the first dropdown, I want the values in another dropdown to be populated accordingly. I need to verify the value of the first dropdown with a variable and then populate the options in the second dropdown based on that conditio ...

Why does my Visual Studio Code always display "building" when I launch an extension?

https://code.visualstudio.com/api/get-started/your-first-extension I followed a tutorial to create a hello world extension. Why does my VSCode always display 'building' when I run the extension? Executing task: npm run watch < [email p ...

Discover the nodes with the highest connections in a D3 Force Graph

As I explore the functionalities of a D3 Force Directed Graph with zoom and pan features, I encounter an issue due to my limited knowledge of d3.js. Is there a way to estimate the number of links for each node in this scenario? I am currently at a loss on ...