I've created a dropdown menu using d3.js v4, but instead of showing the selected option on the HTML page I want it to show a (fixed) name I set myself. This is what I have so far:
d3.select("body" )
.append("select")
.attr("id", 'dropdownMenu')
.attr("class", "button")
.selectAll("dropdownOption")
.data(['a', 'b', 'c', 'd'])
.enter().append("option")
.text(function(d) { return d;})
.attr("value", function(d, i) { return i; });
d3.select('#dropdownMenu').property("dropdownOption" , 0);
I've already tried setting a label attribute, but that did not work.
Please note: I'm quite new to d3 so if anything can be improved on my code I would appreciate the feedback.
To clarify: if the dropdown would select the colors of graph, the options would be ['red, 'blue', 'green']. Currently my code would show the button on the front-end with 'red' written in the button. Instead, I would like it to show 'Graph color'.
Related
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?
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");
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.
I have made a D3 treemap based on this example: http://bl.ocks.org/mbostock/4063582. My version also shows the children after clicking the treemap or button.
The size/count changefunction doesn't work as it should: I get double nodes, the backgroundcolors mix up and there are divs without a backgroundcolor.
color = d3.scale.ordinal()
.domain(colorDomainArr) //colorDomainArr = [0,1,2,3]
.range([
"#b1e1d7", "#c1e7df", "#d1ede6", "#e1f3ee", //lichtblauw
"#688ea3", "#87a4b5", "#a5bac7", "#c3d0d9", //donkerblauw
"#cbb84c", "#d7c671", "#e3d494", //"#eee2b7", //geel
"#b9c2c1", "#ced4d3", "#e3e6e6", //"#f8f8f8", //grijs
]);
It used to work, but not after I fixed another similar colorproblem.
I haven't changed the change() function.
I've tried adding
node.style("background", function(d) {
return d.children && d.depth != ondiepst && d.depth != ondiepst-1 ? color(d.name) : null
})
Color is also done by
d3.selectAll(".BG").style("background", function(d) { return color(d.name) });
In the first view everything goes well
Click on 'Laat alles zien'-button: Colors mix up
Click on 'count' radio button: no background color on some nodes
Click on 'Originele weergave'-button: it seems like everything is well, but we see the 'size' version instead of the 'count' version.
Click on 'size' radio button: three/four times double nodes instead of original view
The jsfiddle doesn't work as it does locally, but it shows my problem and code.
https://jsfiddle.net/zjx967g4/
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.