d3j : Remove a data set - javascript

I want to remove a curve when a checkbox is unchecked. I tried this without success:
function drawdata(fileno,draw){
var name="circle"+fileno;
if (draw) {
console.log("Drawing file "+fileno+ " ("+files[fileno]+")");
svg.selectAll(name) .data(datasets[fileno]) .enter() .append("circle")
.attr('cx',function(a){ return xscales[xval](a[xval]); })
.attr('cy',function(a){ return yscales[yval](a[yval]); })
.attr('r',3)
.style("opacity", 1)
;
} else {
console.log("removing file "+fileno);
svg.selectAll(name).data(datasets[fileno]).exit().remove();
}
}
The first part work well (ie if I check a checkbox the file is plotted) but when I uncheck it, it is not removed from the svg. I guess I do not understand the exit well. Can someone point out what is wrong? Thanks.
Sample jsfiddle code:
http://jsfiddle.net/datavis/530h1qLw/11/#&togetherjs=Rkz5QyDgJm

Update:
With the addition of the fiddle, it's pretty easy to see what is going on. When you do svg.selectAll(name) it's returning an empty selection. The reason for that is because there are no elements of circleN. circleN is not a valid DOM element, and none of them exist anyway, so the selection will fail.
If you make the following changes to your cds function you will see that your code now works:
function cds(el) {
name = "circle" + el.id;
if (el.checked) {
svg.selectAll(name)
.data(dataset[el.id])
.enter().append("circle")
.attr('cx', function (a) { return xscale(a[0]); })
.attr('cy', function (a) { return yscale(a[1]); })
.attr('r', 3)
.attr('class', name)
.style("opacity", 1);
} else {
svg.selectAll("circle." + name).remove();
}
}
There are two changes of note in this function:
.attr('class', name) being added to the code creating your new DOM elements. This will add a class to the circle, making the selection easier when it comes time to remove them.
Updated the line that removes them to svg.selectAll("circle." + name).remove(); which will remove the circles when you uncheck the radio boxes.

Related

d3 order of label and checkbox chained

My question is complimentary for this post, but since I do not have enough reputation, I could not ask it in a comment!
I was trying to create checkboxes and labels dynamically using d3. Getting it to work with label + checkbox is easy. What I wanted to do was to put the checkbox before label by replicating this HTML code:
<label><input type="checkbox" id="checkbox1">Option 1</label>
The solution in the aforementioned post works, but I don't understand why the chained(nested selection/append) version is not working? (i.e. the code below)
var temp = item.append("label");
temp.append("input")
.attr("type", "checkbox")
.attr("checked", true)
.attr("id", function (d,i) {
return "checkbox" + i;
})
.on("click", function (d,i) {
....
});
temp.text(function (d) {
return d.text;
});
I expect the code to add the label's text after input but it does not do so. Can someone please explain why? Am I missing something?

Keep data for unchecked option from checkbox d3

I'm really struggling linking the checked/unchecked to lines.
I managed to have lines selected when options checked from checkbox.
But when the options are unchecekd then they are removed completely.
Instead of removing them, I'd to like to have them chinging attributes, such as colours.
I took the relevant part only since original code is unnessesarily long.
The data is nested by key - country and the checkbox options contain the country as id/value.
Initially lines (same path but grey colour) are rendered and the part below is colouring the lines that are selected in the checkbox.
Lines are coloured successfully but they disappear when the respective options are unchecekd. Instead, I'd like to have them remain with initial colour which is grey.
But it seems .exit() doesn't work here.
I'm really struggling... Could anyone help me?
d3.select(#checkbox).on("change", function() {
var country = this.value
display = this.checked ? "inline" : "none";
var filtered = lineWrapper
.selectAll(".line")
.filter(function(d) { return d.key === country; });
filtered
.transition()
.duration(2000)
.ease(d3.easeLinear)
.style("stroke", function(d) {return colScale(d.key); })
.style("stroke-width", 3)
.style("fill", "none");
filtered
.exit()
.transition()
.style("stroke", "#C0C0C0")
.style("stroke-width", 1)
.style("fill", "none");

Update the heatmap based on the selected data

I created an heatmap and some sparklines following this example.
In the example the user could click on the labels of the rows and those of the columns, in my case I kept only the possibility to click on the columns labels.
That example was perfect for my data, only I need to update the heatmap based on the radio buttons selection.
The first radio buttons allow you to choose the type of area (A or B).
The second group of radio buttons allow you to choose the daily data to be displayed.
However, the data are "incomplete": not all the months have daily data but only April and December.
So if you select the April or December radio button, the daily data on the heatmap are shown, otherwise the monthly ones are shown.
The example works but it is very primitive because the heatmap is deleted and recreated every time.
// Return to the initial order when the user clicks on the button
d3.select('#initialOrder').on('click', function() {
var trans = heat.transition().duration(1000);
var sortedYear = Array.from(Array(numYLabels).keys());
trans.selectAll('.cell')
.attr('y', function(d) {
var row = parseInt(d3.select(this).attr('data-r'));
return sortedYear.indexOf(row)*cellSize;
});
trans.selectAll('.rowLabel')
.attr('y', function(d, k) {
return sortedYear.indexOf(k) * cellSize;
});
sortedYear.forEach(function(d) {
d3.select('#data-svg-' + d).raise();
});
});
// Area radio button change selection
d3.selectAll('input[name=area-rb]').on('change', function() {
areaSelected = d3.select('input[name="area-rb"]:checked').property("value");
console.log('areaSelected:', areaSelected);
d3.select('#heatmapSvg').remove();
d3.selectAll('.data-svg').remove();
createHeatmap();
});
// Month radio button change selection
d3.selectAll('input[name=month-rb]').on('change', function() {
monthSelected = d3.select('input[name="month-rb"]:checked').property("value");
console.log('monthSelected:', monthSelected);
if(avaibleDayData.includes(monthSelected)) {
d3.select('#heatmapSvg').remove();
d3.selectAll('.data-svg').remove();
createHeatmap();
}
else {
monthSelected = 'nothing';
d3.select('#heatmapSvg').remove();
d3.selectAll('.data-svg').remove();
createHeatmap();
}
});
I found this example that allow to update the heatmap but I can't able to adapt the code.
In the example, data changes only value and not "shape". That is, the number of labels remains the same. In my case, the situation is a bit more complicated.
I create a Plunker with the code.
This is a good example where a LOT of d3's enter/update/exit selection can be implemented.
Yeah, I agree: recreating a chart on every change isn't a good solution.
Okay, so here's a fork of your Plunkr using enter/update/merge/exit methods.
http://plnkr.co/edit/2v8YQoZSClhKpW2U1pwi?p=preview
A preview of how I got the merge selection done: Let's take rowLabels for example:
// make region labels
var rowLabels = rowLabelGroup
.selectAll('text.rowLabel')
.data(yLabelsNames);
// ENTER SELECTION FOR COL LABELS
// ENTER + UPDATE
// After merging the entered elements with the update selection,
// apply operations to both.
rowLabels
.enter().append('text').attr('class', 'rowLabel mono')
.attr('font-weight', 'normal')
.style('text-anchor', 'end')
.on('mouseover', function(d) {
d3.select(this).attr('font-weight', 'bold').style('fill', 'red');
})
.on('mouseout', function(d) {
d3.select(this).attr('font-weight', 'normal').style('fill', 'black');
})
.attr('x', 0)
.attr('y', function(d, i) {
return i * cellSize;
})
.attr('transform', function(d, i) {
return 'translate(-3, 11)';
})
.merge(rowLabels)
.attr('name', function(d) {
return d;
})
.text(function(d) {
return d;
})
.attr('id', function(d) {
return 'rowLabel_' + yLabelsNames.indexOf(d);
})
.attr('label-r', function(d) {
return yLabelsNames.indexOf(d);
});
// exit/remove elements which are no longer in use
rowLabels.exit().remove();
Similarly, these methods have been applied to colLabels, cells and sparkLineSvgs as you can notice in the code.
And regarding appending of the SVGs, I've moved that code to outside the updateHeatmap function. And yes, btw, I've changed the name of the function from createHeatmap to updateHeatmap.
And I did encounter an issue while hovering over for the tooltip i.e. the tooltip flickered a lot. To counter that, I've added pointer-events:none to .d3-tip tooltip.
Go through the code and let me know if I've missed on anything or if you face issue understanding any part.
Hope it helps.

style text based on the context of the text in d3

I wondering is there is a way to style text in d3 based on the context of the text itself?
I have this chunk of d3 code that loop through a list of items and append them to the DOM on mouseover, so the list is updating a lot as the mouse moves along the chart. I'd like to color the p tag with the class coeff-direction as red or green based on the text inside saying "Avoid" or "Target" but what I have currently only colors everything red? Is there anyway to appropriately access the text so my condition will work?
d.features.forEach(function(m){
d3.select("#showdata")
.style("display", null)
.append("p")
.attr("class", "coeff-direction")
.text(m.coeffDirection + ' ')
.style("color", function(d) {
if ("Avoid") {
return "red"
} else {
return "Green"
}
})
.append("span")
.attr("class", 'feature-description')
.text(m.featureDescription)
})
Here is a JSFiddle with all the code: JSFiddle
The problem is that nothing is getting passed in the color function. You just need to check against the same variable that you are using the set the text - m.coeffDirection. So your if block needs to be replaced with:
if (m.coeffDirection === "Avoid") {
return "red"
} else {
return "Green"
}
Find a working JSFiddle here.
Your code only ever evaluates to true, as the string "Avoid" is always true.
if ("Avoid") {
return "red"
} else {
return "Green"
}
Perhaps you meant to check the argument d for d === "Avoid" ?
Example:
if (d === "Avoid") {
return "red";
} else {
return "green";
}

D3 force layout - legend interactivity on/off

I have a force layout with a legend, and I’ve appended checkboxes to each role in the legend.
I’m trying to follow this example(https://jsfiddle.net/zhanghuancs/cuYu8/) and add interactivity to the legend, but when I uncheck any of the checkboxes, all the links disappear instead of the links and nodes related to the role.
I’m creating the legend using this loop,
var col = color(d.group);
// adding code to display legend
// as nodes are filled
if(!(d.role in roles)){
legend.append("li")
.html(d.role)
.style("color", col)
//add checkbox
.append("input")
.attr("type", "checkbox")
.attr("id", function (d){
return "chk_" + d;
})
//check box
.property("checked", true)
.on("click", function (d){
//click event
var visibility = this.checked? "visible" : "hidden";
filterLegend(d,visibility);
})
roles[d.role] = true;
}
return col; })
Here’s a fiddle of my graph (https://jsfiddle.net/gbhrea/077mb7o1/1/), using a lot of data so just used a small sample for the fiddle. (graph won’t show on fiddle but will keep the link anyways so that full code can be seen)
Here is the code for changing visibility
function filterLegend(aType, aVisibility){
//change vis of link
link.style("visibility", function (o) {
var lOriginalVisibility = $(this).css("visibility");
return o.type === aType ? aVisibility : lOriginalVisibility;
});
//change vis of node
//if links of node invisible, node should be too
node.style("visibility", function (o, i) {
var lHideNode = true;
link.each(function (d, i) {
if (d.source === o || d.target === o) {
if ($(this).css("visibility") === "visible") {
lHideNode = false;
}
}
})
});
} //end filter
To be clear, what I want to achieve is - When I uncheck a role, e.g Actor, all nodes and links disappear. Any pointers would be greatly appreciated :)
There are a few mistakes in your code. First, you're not binding any data to the legend elements so .on("click", function (d) { }) won't work because d isn't defined there. You do have d.role there though, so that's what you should use to pass on to functions.
Similarly, your filterLegend() function references undefined things (.type) -- this is why everything disappears at the moment. You're passing in something undefined and comparing it to something undefined, which gives true. Furthermore, links don't have the node information directly, but under .source and .target, so you need to compare to .source.role and .target.role.
For the nodes on the other hand, it's much easier than what your current code is trying to do -- there's no need to iterate over the links. You can use the code you have for the links here, except for comparing to the existing .role attribute instead of the non-existing .type.
Complete demo here.

Categories

Resources