The d3.js circles mysteriously disappear when attempting to implement the update pattern

I find myself in a challenging situation once again - there may come a day when I am the one offering help, but for now, I admit that I am feeling quite lost. I am very new to d3.js and my understanding of JavaScript is still quite basic.

My current project involves creating an interactive visualization of characters and their relationships. The idea is to begin with one character (the user who is logged in) and display their connections initially. When the user clicks on a node, the magic should unfold, revealing the relations of the clicked node... and so forth. (Eventually, the user should also have the ability to hide nodes again, but let's take it one step at a time!)

I have uploaded the code and my json files to GitHub, where you can access them: https://github.com/katjalennartz/ba (index2.html should show everything; the JS code is in noForce2.js).

I suspect that the issue causing no circle to be displayed lies within my groups, and that I may have made mistakes there. I'm unsure how to properly bind my data so that the nodes can be dragged (and the links and text will follow the nodes...)

This is the part that I believe is not functioning correctly:

        .selectAll("circles") // was null before try to use an update function -> null for save index 0 from missing when append circles. but null now is not working
            .data(rootNodes)
  
      //Exit (remove old elements)
      circleGroup.exit().remove;
      
      //Enter data 
      circleGroup.enter()
          .append('g')
          .attr('transform', function (d) {
              return 'translate(' + d.x + ',' + d.y + ')'; //work not nested data
          })
          .call(d3.drag() //need the drag so you can play with the nodes - links should follow - try to group it for show text beside circle
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended));
      
      //add the circle 
      circleGroup.append("circle")
          .attr("r", function (d) {
              //increase radius if current user 
              if (d.id === thisuser) {
                  return radius * 2
              } else { return radius; }
          })
          .attr("class", "chara")
          .attr("fill", function (d) {
              //set color to red if current user 
             return (d.id === thisuser) ? "red" : "blue"
          })
          .style("stroke", "#ffffff")
          .on("click", update); //this should call the update
      
      //set text to circle (username)
      circleGroup.append("text")
          .text(function (d, i) { return d.username; })
          //.style('text-anchor', 'top')
          .attr("x", radius)
          .attr("y", 4);

If anyone here could assist me in untangling this mess, I would be immensely grateful.

Answer №1

The issue lies in the utilization of the circleGroup variable. When initially referencing circleGroup, the following code snippet is used:

var circleGroup = svg
    .selectAll("g") 
    .data(rootNodes)

At this point, circleGroup represents the update selection. It's only when utilizing either enter() or exit() methods that it transforms into the enter selection as needed. This concept is correctly demonstrated here:

circleGroup.enter() // Enter selection
    .append('g')
    .attr('transform', function (d) {
        return 'translate(' + d.x + ',' + d.y + ')';
    })
    .call(d3.drag() 
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

However, a mistake is made in the following section:

circleGroup.append("circle") // no enter(), so update selection
    .attr("r", function (d) {
        if (d.id === thisuser) {
            return radius * 2
        } else { return radius; }
    })
    .attr("class", "chara")
    .attr("fill", function (d) {
        return (d.id === thisuser) ? "red" : "blue"
    })
    .style("stroke", "#ffffff")
    .on("click", update);

An alternative approach involves directly using enter() in the circleGroup variable to effectively represent the desired enter selection. For managing the update and exit selections, another variable can be employed:

var circleGroup = svg
    .selectAll("g") 
    .data(rootNodes)

var circleGroupEnter = circleGroup.enter()

circleGroupEnter
    .append("circle")
    .append('g')
    .attr('transform', function (d) {
        return 'translate(' + d.x + ',' + d.y + ')'; //work not nested data
    })
    .call(d3.drag() //need the drag so you can play with the nodes - links should follow - try to group it for show text beside circle
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

        
circleGroupEnter
    .attr("r", function (d) {
        if (d.id === thisuser) {
            return radius * 2
        } else { return radius; }
    })
    .attr("class", "chara")
    .attr("fill", function (d) {
        return (d.id === thisuser) ? "red" : "blue"
    })
    .style("stroke", "#ffffff")
    .on("click", update);

Utilizing selection.join()

A more streamlined approach involves implementing selection.join() to simplify the process. To gain insight into its functionality, refer to this tutorial: tutorial link.

var circleGroup = svg
    .selectAll("g") 
    .data(rootNodes)

circleGroup
    .join('g')
    .attr('transform', function (d) {
        return 'translate(' + d.x + ',' + d.y + ')';
    })
    .call(d3.drag() 
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

        
circleGroup
    .join("circle")
    .attr("r", function (d) {
        if (d.id === thisuser) {
            return radius * 2
        } else { return radius; }
    })
    .attr("class", "chara")
    .attr("fill", function (d) {
        return (d.id === thisuser) ? "red" : "blue"
    })
    .style("stroke", "#ffffff")
    .on("click", update);

selection.join() also simplifies handling all three selections, as illustrated in the subsequent example within your code:

var link = svg.append("g")
    .attr("class", "link")
    .selectAll("line")
    .data(rootLinks);
        
link.join(
    enter => enter.append('line'),
    update => update,
    exit => exit.remove()
)

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 is the purpose of using an img tag to retrieve a URL that is not an image?

Upon investigating a slow page load, I came across something interesting in the HTML: <img src="/ajax?action=askedAboutCookies" style="display:none" width="0" height="0" alt="" /> When the page loads, it sends a request but never receives a respons ...

Tips for choosing an ID in Jquery to use in an if statement

I'm trying to create a form selector that will display different outputs depending on the selected option. However, I'm having some trouble getting it to work. Below is my current code: <!doctype html> <html> <head> <meta ch ...

Ways to organize backbone models, views, and collections using vim?

I am a fan of vim's folding feature, but I have encountered some challenges when trying to fold backbone models, views, and collections. This is because backbone does not follow the traditional prototype syntax, but instead uses a .extend() based synt ...

Effective technique for connecting client-side JavaScript with server-side node.js

I am striving to create a compact website featuring field auto-completion, drawing suggestions from the server's database (utilizing Node.js + MySQL). What approaches can I take to establish client-server communication as the user inputs data into a ...

Finding the Ideal Location for Controllers in an Express.js Project

I'm relatively new to software development and one concept that I find challenging is organizing the directory structure of various projects. As I prepare to embark on an Express project, I prefer keeping controller classes separate from route callbac ...

navigation bar directing to the wrong destination

My landing page has attached pages like landing/gothere and /another. The issue arises when I try to navigate from /another to landing/gothere. I fetch landing information using an API, but there's a delay when I click on the navbar link. The page loa ...

Error encountered in Angular 7.2.0: Attempting to assign a value of type 'string' to a variable of type 'RunGuardsAndResolvers' is not allowed

Encountering an issue with Angular compiler-cli v.7.2.0: Error message: Types of property 'runGuardsAndResolvers' are incompatible. Type 'string' is not assignable to type 'RunGuardsAndResolvers' This error occurs when try ...

Maximizing Screen Size for Videos on Internet Explorer: A Step-by-Step Guide

How can I make the video background fit properly on all browsers, including IE, without zooming it? Here is my website link: The CSS property object-fit: cover works for most browsers, but unfortunately IE does not support this property. I would apprecia ...

Creating a drop-down menu in Angular 2

I'm working on my webApp and I have a menu that I want to enhance by adding a submenu. How can I achieve this? Do I need to attach a click event to the a tag with the has-sub class for each individual menu item, or is there a better solution? What sh ...

Using Java Script to adjust dimensions in a webpage

As a newcomer to JS, I encountered my first simple problem that I can't seem to solve. Currently working on a basic website using an online editor, I decided to add a shoutbox from shoutbox.com In order to use the shoutbox, I embedded it in an HTML ...

The straightforward onclick action only functioned once

Looking for assistance: Can someone explain why this code snippet isn't functioning properly? It seems that the increment is only happening once. var player = document.getElementById("player"); var button = document.getElementById("button"); functio ...

Creating a file by piping the output of an asynchronous HTTP request while utilizing async await method

I have been diligently following the instructions and conducting thorough research for a whole day to figure out how to use a pipe to compile an Excel spreadsheet that is fetched through an API call. I managed to get part of the way in saving it, but unfor ...

The YYYY-dd-mm input mask pattern is malfunctioning

Hello, I am having trouble creating a pattern for an input field in the format YYYY-mm-dd. My code is not working properly. Can someone please help me correct my code? Any assistance would be greatly appreciated. <html> <head> <title>En ...

What is the best method for incorporating a JavaScript redirect into an Android WebView?

My issue involves loading a page into a webview. Typically, when I use the code webview.loadUrl(url); it functions as expected. The url contains a javascript code that redirects the page. Here is the javascript code: <script type="text/javascript"> ...

Dynamically Growing Navigation Bar Elements with Nested Subcategories Based on Class Identification

Let's say you have a menu bar structured as follows: <nav> <ul class="nav"> <li class="menu1"><a href="#">Menu Item 1</a></li> <li class="menu2"><a href="#">Menu Item 2</a> <ul& ...

What could be causing my React Router component to display incorrect styling?

While working with react-router, I encountered an issue where the text from the routed page started appearing in the navbar. Even though I separated it as its own component and it functions correctly, this specific problem persists. As a newcomer to React, ...

Troubleshooting Error: Heroku, mLab, and Node.js - Application Issue with Mongoose

As a beginner in the world of Node.js and Heroku, I am attempting to host my first application on Heroku but encountering deployment issues. To guide me through this process, I have been following a tutorial which can be found here: https://scotch.io/tutor ...

Getter and Setter Implementation in Typescript without Using Classes

Check out these various SO questions discussing Typescript getters/setters: from 2015, Jan 2018, Sept 2018, and more. Now, the question arises - what is the best approach to define Typescript types for getters/setters in a plain JavaScript object without ...

What steps should I follow to incorporate channel logic into my WebSocket application, including setting up event bindings?

I'm currently tackling a major obstacle: my very own WebSocket server. The authentication and basic functionality are up and running smoothly, but I'm facing some challenges with the actual logic implementation. To address this, I've create ...

What is the best way to design an HTML page so that it changes its title daily?

I have a goal to change the site's title every day, but I am not an experienced programmer. I know that this can be done using JavaScript. I came across this idea: setInterval(function() { //change title //document.title = "Some new title"; ...