Paul Smith
Paul Smith

Reputation: 187

Refresh function not being called properly d3

I am working with a scatterplot in d3. Dots on the graph represent a paper. On right click of a dot I have a context menu where there are 2 options: 1) to add that paper to the library (change type to In_library) and 2) Remove from library (remove paper from data completely).

I call the refreshGraph() function after each of these updates which redraws the graph with the updated data. But nothing happens which I assume is because the refreshGraph() is not being called properly? Or for option 1 type library is not being set properly? When refreshGraph is called after option 1 the dot should turn blue and on calling it for option 2 the dot should disappear from display as it has been removed from the alldata which is the data that is being used to draw the circles. Here is the relevant code:

allData = [];

var menu = [{
title: 'Add to Library',
action: function addToLibrary(elem, d, i) {

    d3.json("connection6.php?paperID="+d.ID, function(error, dataJson) {

        for(i=0;i<allData.length;i++){ 
            if (d.type === "In_library")
            {
            alert("The paper: " + d.TITLE + " is already in your Library!"); 
                    return; 
            }
            }

            d.type = "In_library"; // is this the correct way to change the type if the input has a different type??
            refreshGraph();

        })
        refreshGraph();
    }

}, 
{
title: 'Remove from Library',
action: function removeFromLibrary (elem, d, i) {

        d3.json("connection9.php?paperID="+d.ID, function(error, dataJson) {

            //loop through allData and if selected ID has type In_library, remove from allData
            for(i=0;i<allData.length;i++){
                if (d.type == "In_library"){
                    allData.splice(i--,1);      
            }
            }
            refreshGraph();
            })  
    }

}
]

function refreshGraph() { 

// draw dots
var circles = svg.selectAll("circle")
        .data(allData)

    circles.transition()
        .attr("cx", function(d) {return x(YearFn(d))})
        .attr("cy", function(d) {return y(Num_citationsFn(d))})

    circles.enter()
        .append("circle")
        .attr("class", "dot")
        .attr("r", 3.5)
        .attr("cx", function(d) {return x(YearFn(d))})
        .attr("cy", function(d) {return y(Num_citationsFn(d))})
        .style("fill",function(d){
                    var colour = "black"
                    switch(d.type){

                    case "In_library":
                        colour = "blue";
                    break;
                    case "cited by":
                        colour = "red";
                    break;
                    case "cites":
                        colour = "green";
                    break;
                    case "selected":
                        colour = "magenta";
                    break;
                    default:
                        colour = "black";
                    }
                    return colour;
                    }) 

        .on("mouseover", mouseHandler)
        .on("mouseout", mouseoutHandler)
        .on("click", clickHandler)
        .on("contextmenu", rightClickHandler);


svg.select(".y.axis").call(yAxis); 
svg.select(".x.axis").call(xAxis);

//don't want dots overlapping axis, so add in buffer to data domain
x.domain([d3.min(allData, YearFn)-1, d3.max(allData, YearFn)+1]);
y.domain([d3.min(allData, Num_citationsFn)-1, d3.max(allData, Num_citationsFn)+1]);

    }

Any help is much appreciated I am new to d3 so thanks in advance!

Upvotes: 0

Views: 137

Answers (1)

James
James

Reputation: 1098

You don't need to re-plot all the data each time a single point changes. Just update that one point.

function rightClickHandler() {

   // if option 1
   d3.select(this).style("fill", "blue");

   // if option 2
   d3.select(this).remove();

}

Your problem likely arises because when you call refreshGraph a second time (or third) your aren't clearly the circles that are already plotted. Your refreshGraph function isn't updating the points already plotted it's recreating them each time, and if you aren't clearing the points that are already there, you won't see the new points (or the absence of them, or the change in color), because they are hidden behind your old points.

EDIT:

If you want to re-add the data each time, you first have to clear the existing data. At the start of your refreshGraph function, add this line:

if(!d3.selectAll("circle").empty()) d3.selectAll("circle").remove();

i.e. if there are circle elements, remove them. This assumes you are only creating circle elements within the refreshGraph function. If you create them elsewhere, the you should probably use the .dot selector instead.

Upvotes: 1

Related Questions