Remove Non-SVG Slider - javascript

I have code for a slider:
var slider = d3.select('body').append('p').text('Sent or Received Threshold: ');
slider.append('label')
.attr('for', 'threshold')
.text('');
slider.append('input')
.attr('type', 'range')
.attr('min', d3.min(graph.links, function(d) {return d.value; }))
.attr('max', d3.max(graph.links, function(d) {return d.value; }))
.attr('value', d3.min(graph.links, function(d) {return d.value; }))
.attr('id', 'threshold')
.style('width', '100%')
.style('display', 'block')
.on('input', function () {
var threshold = this.value;
d3.select('label').text(threshold);
var newData = [];
graph.links.forEach( function (d) {
if (d.value >= threshold) {newData.push(d); };
});
color.domain([d3.min(newData, function(d) {return d.value; }), d3.max(newData, function(d) {return d.value; })]).interpolator(d3.interpolateBlues);
link = link.data(newData, function(d){ return d.value});
link.exit().remove();
var linkEnter = link.enter().append("path")
.style("stroke", function(d) { return color(d.value); })
.style("fill", "none")
.style("stroke-width", "3px");
link = linkEnter.merge(link).style("stroke", function(d) { return color(d.value); });
node = node.data(graph.nodes);
simulation.nodes(graph.nodes)
.on('tick', tick)
simulation.force("link")
.links(newData);
simulation.alphaTarget(0.1).restart();
});
I would like to remove the slider within a change function based on a PHP drop-down. For the d3 viz itself, I use d3.selectAll("svg > *").remove(). I know this slider isn't part of SVG, so how would I remove it? Currently, on change of the drop-down, a new slider is added below the previous slider. Is there a d3.selectAll statement that needs to be placed somewhere?
Thank you for any insight you all might have!

D3 is not restricted to manipulating SVG elements. In fact, D3 can manipulate anything in that page.
That being said, you can do that by simply using:
selection.remove()
Where selection is, of course, any selection containing the slider.
For instance, you can select by element...
d3.select("input").remove();
... or by ID:
d3.select("#threshold").remove();
But, in your case, the simplest solution is using the selection you already have:
slider.remove();
Here is a demo with your code for the slider, click the button:
var slider = d3.select('body').append('p').text('Sent or Received Threshold: ');
slider.append('label')
.attr('for', 'threshold')
.text('');
slider.append('input')
.attr('type', 'range')
.attr('min', 0)
.attr('max', 100)
.attr('value', 30)
.attr('id', 'threshold')
.style('width', '100%')
.style('display', 'block');
d3.select("button").on("click", function(){
slider.remove();
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>

Related

Stop div's childNodes from responding to div's event listener

I'm using d3 to display the data from a dataset. The display shows the three elements of a hierarchy:
Top level: "Organization-sustaining activities"
Middle level: "Extreme Sports"
Bottom Level: "Skydiving management"
When the DOM representing the middle level is clicked, the bottom level appears. When the middle level is clicked a second time, the bottom level disappears. This is all good.
The problem is that my users are always tempted to click the bottom level, and doing so disappears the bottom-level. I'd like to make it so that the bottom-level only disappears when the middle-level is clicked.
What I've tried:
I tried putting the event listener on the middle-level text element rather than the div. This led to the error d.key is not a function.
I also tried preceding on.click with div.parentNode and divs2.parentNode but got the message divs2.parentNode is undefined.
Here is my code
var doc = URL.createObjectURL(new Blob([`TooltipInfo Category Function1 Function2
Records relating to the skydiving. Includes halters, parachutes, and altimeters.<ul><li>For records relating to rock climbing, see <b>rock climbing</b>.</li><li>For travel expenses, see <b>Procurements & Purchasing</b>.</li></ul>Retention:<ul><li>Keep records for seven years from the date of record creation, then send to <mark>archives.</mark></li><li>Keep all other records for seven years from the date of record creation, then destroy.</li></ul> • Skydiving Management Extreme Sports > Organization-sustaining Activities`]))
d3.tsv(doc)
.row(function(d) {
return {
University: d.University,
TooltipInfo: d.TooltipInfo,
Searchterms: d.Searchterms,
Category: d.Category,
Function1: d.Function1,
Function2: d.Function2,
MaxRetentionRounded: d.MaxRetentionRounded,
ModifiedRetention: d.ModifiedRetention
};
})
.get(function(error, data) {
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 150,
width = 300;
var nest = d3.nest()
.key(function(d) {
return d.Function2;
})
.key(function(d) {
return d.Function1;
})
.key(function(d) {
return d.Category;
})
.entries(data);
var height = 80,
width = 150;
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
divs.append("p")
.html(function(d) {
return d.key;
});
var divs2 = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('div')
.attr("class", "first")
.style("cursor", "pointer")
.on("click", function(d, i) {
const curColour = this.childNodes[1].attributes["height"].nodeValue;
if (curColour == '0px') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else if (curColour == '0') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else {
d3.selectAll(this.childNodes).attr("height", "0px");
}
});
divs2.append("text")
.attr('class', 'label1')
.attr('x', 0)
.attr('y', 0)
.style("font-size", "21px")
.text(function(d) {
return d.key;
})
var svgs2 = divs2.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('svg')
.attr("class", "second")
.attr("height", 0)
.attr("width", function(d) {
return String(d3.select(this).value).length * 31.5
})
svgs2.append("text")
.attr('class', 'label2')
.attr('x', 10)
.attr('y', 17)
.style("font-size", "14px")
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'start')
.style("cursor", "pointer")
.on("mouseover", function(d, i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<div class="container"></div>
UPDATE: I've tried putting a 'Stop Propagation' on the childnode:
.on("mouseover", function(event) {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
But it seems to stop the child's action (tooltip appearing) rather than the parents action (child disappearing).
UPDATE#2: stopPropagation doesn't seem to apply to mouseover, only to click. The following gives the behaviour I want (but I still need to figure out how to disappear the tooltip):
.on("click", function() {
event.stopPropagation();
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
When you click on your bottom level SVG the browser tries to find a suitable handler for the resulting click event starting at the bottommost element at the mouse (more generally speaking, the pointer) position. If no handler is found on that element the browser will traverse the DOM tree upwards checking any enclosing—i.e. parent—elements for a registered handler until one handler is found or the root element has been reached. This process is called event bubbling and, if you are not familiar with it, you might want to spend some time digging into this concept as it will help to understand many misconceptions when it comes to JavaScript event handling. There are numerous resources to be found covering this topic, e.g.:
What is event bubbling and capturing?
Bubbling and capturing
To stop the click event from bubbling up to the mid-level element causing it to toggle the bottom level element's visiblity you need to register a click handler on the bottom level element itself. In that handler you can use the event's stopPropagation method to prevent any further bubbling of the event.
.on("click", () => d3.event.stopPropagation());
Doing so, the handler of the mid-level element will not get executed if you click on the bottom level element while it is still reachable if you click on the mid-level element itself.
Have a look at the following working demo:
var doc = URL.createObjectURL(new Blob([`TooltipInfo Category Function1 Function2
Records relating to the skydiving. Includes halters, parachutes, and altimeters.<ul><li>For records relating to rock climbing, see <b>rock climbing</b>.</li><li>For travel expenses, see <b>Procurements & Purchasing</b>.</li></ul>Retention:<ul><li>Keep records for seven years from the date of record creation, then send to <mark>archives.</mark></li><li>Keep all other records for seven years from the date of record creation, then destroy.</li></ul> • Skydiving Management Extreme Sports > Organization-sustaining Activities`]))
d3.tsv(doc)
.row(function(d) {
return {
University: d.University,
TooltipInfo: d.TooltipInfo,
Searchterms: d.Searchterms,
Category: d.Category,
Function1: d.Function1,
Function2: d.Function2,
MaxRetentionRounded: d.MaxRetentionRounded,
ModifiedRetention: d.ModifiedRetention
};
})
.get(function(error, data) {
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 150,
width = 300;
var nest = d3.nest()
.key(function(d) {
return d.Function2;
})
.key(function(d) {
return d.Function1;
})
.key(function(d) {
return d.Category;
})
.entries(data);
var height = 80,
width = 150;
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
divs.append("p")
.html(function(d) {
return d.key;
});
var divs2 = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('div')
.attr("class", "first")
.style("cursor", "pointer")
.on("click", function(d, i) {
const curColour = this.childNodes[1].attributes["height"].nodeValue;
if (curColour == '0px') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else if (curColour == '0') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else {
d3.selectAll(this.childNodes).attr("height", "0px");
}
});
divs2.append("text")
.attr('class', 'label1')
.attr('x', 0)
.attr('y', 0)
.style("font-size", "21px")
.text(function(d) {
return d.key;
})
var svgs2 = divs2.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('svg')
.attr("class", "second")
.attr("height", 0)
.attr("width", function(d) {
return String(d3.select(this).value).length * 31.5
})
svgs2.append("text")
.attr('class', 'label2')
.attr('x', 10)
.attr('y', 17)
.style("font-size", "14px")
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'start')
.style("cursor", "pointer")
.on("mouseover", function(d, i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d3.select(this).datum().values[0].TooltipInfo)
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
.on("click", () => d3.event.stopPropagation());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
<div class="container"></div>
The answer is a combination of three different elements:
Change your event listener from a mouseover to a click. This
allows you to..
Add stopPropagation on the child div.
Once you've gotten rid of mouseover, you can no longer use 'mouseout' to
turn off the tooltip. You'll need a conditional in your on.click
function. See below for full example.
var doc = URL.createObjectURL(new Blob([`TooltipInfo Category Function1 Function2
Records relating to the skydiving. Includes halters, parachutes, and altimeters.<ul><li>For records relating to rock climbing, see <b>rock climbing</b>.</li><li>For travel expenses, see <b>Procurements & Purchasing</b>.</li></ul>Retention:<ul><li>Keep records for seven years from the date of record creation, then send to <mark>archives.</mark></li><li>Keep all other records for seven years from the date of record creation, then destroy.</li></ul> • Skydiving Management Extreme Sports > Organization-sustaining Activities`]))
d3.tsv(doc)
.row(function(d) {
return {
University: d.University,
TooltipInfo: d.TooltipInfo,
Searchterms: d.Searchterms,
Category: d.Category,
Function1: d.Function1,
Function2: d.Function2,
MaxRetentionRounded: d.MaxRetentionRounded,
ModifiedRetention: d.ModifiedRetention
};
})
.get(function(error, data) {
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 150,
width = 300;
var nest = d3.nest()
.key(function(d) {
return d.Function2;
})
.key(function(d) {
return d.Function1;
})
.key(function(d) {
return d.Category;
})
.entries(data);
var height = 80,
width = 150;
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
divs.append("p")
.html(function(d) {
return d.key;
});
var divs2 = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('div')
.attr("class", "first")
.style("cursor", "pointer")
.on("click", function() {
const curColour = this.childNodes[1].attributes["height"].nodeValue;
if (curColour == '0px') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else if (curColour == '0') {
d3.selectAll(this.childNodes).attr("height", "20px");
} else {
d3.selectAll(this.childNodes).attr("height", "0px");
}
}, false);
divs2.append("text")
.attr('class', 'label1')
.attr('x', 0)
.attr('y', 0)
.style("font-size", "21px")
.text(function(d) {
return d.key;
})
var firstClick = 1;
var svgs2 = divs2.selectAll(null)
.data(function(d, e) {
return d.values;
})
.enter()
.append('svg')
.attr("class", "second")
.attr("height", 0)
.attr("width", function(d) {
return String(d3.select(this).value).length * 31.5
})
svgs2.append("text")
.attr('class', 'label2')
.attr('x', 10)
.attr('y', 17)
.style("font-size", "14px")
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'start')
.style("cursor", "pointer")
.on("click", function() {
event.stopPropagation()
if (firstClick % 2 === 1) {
div.transition()
.duration(200)
.style("opacity", .9)
div.html(d3.select(this).datum().values[0].TooltipInfo)
console.log(firstClick);
} else {
div.style("opacity", 0)
}
firstClick++;
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div class="container"></div>

Using data to produce scatterplot with d3 transition method

I'm trying to do something like this: http://bost.ocks.org/mike/nations/
However instead of the transitions on mouseover I want the transitions to display when I click on a button for each year in the timeline.
Some example data in a csv file:
time,name,xAxis,yAxis,radius,color
1990,America,10,20.2,30,black
1990,China,50,50,50,yellow
2000,Singapore,20,30,20,red
2010,China,60,50,50,yellow
2020,America,20,30,40,black
2020,Malaysia,60,5,10,orange
I'm new to javascript and d3 and am having trouble with the transitions. I want the circles to be unique to each name (America, China, Singapore, Malaysia) so that I will only have one circle per name. Currently new circles add when I click on the respective timeline buttons, but don't transit to new positions or exit.
Read data using d3.csv:
d3.csv("data.csv", function(dataset) {
var years = [];
data=dataset;
//create a button for each year in the timeline
dataset.forEach(function(d){
console.log(d.time);
//if not existing button for timeline
if($.inArray(d.time, years) == -1)
{
var button = document.createElement("button");
button.setAttribute("type", "button");
button.setAttribute("class", "btn btn-default");
button.setAttribute('onclick', 'update("'+d.time+'")');
var t = document.createTextNode(d.time);
button.appendChild(t);
$("#timeline").append(button);
years.push(d.time);
}
})
//create circles for the first year
svg.selectAll("circles")
.data(dataset.filter(function(d) { return d.time == d3.min(years);}, function(d) { return d.name; }))
.enter()
.append("circle")
//.filter(function(d){ return d.time == d3.min(years); })
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.style("fill", function(d) { return d.color; })
.transition()
.duration(800)
.attr("r", function(d) { return d.radius});
});
My update function:
function update(year){
var circle = svg.selectAll("circles")
.data(data.filter(function(d){return d.time == year;}), function(d) { return d.name; });
//update
circle.attr("class", "update")
.filter(function(d){ return d.time == year; })
.transition()
.duration(800)
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.attr("r", function(d) { return d.radius});
//enter
circle.enter().append("circle")
.filter(function(d){ return d.time == year; })
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.style("fill", function(d) { return d.color; })
.attr("r", function(d) { return d.radius});
//exit
circle.exit()
.remove();
}
Can someone point me in the right direction? Thanks.
svg.selectAll("circles") is invalid and should become svg.selectAll("circle") (singularize "circles").
As you have it currently, with "circles", it yields an empty selection, so d3 assumes all your data is bound to non-existent circles, and therefore the .enter() selection is always full (rather than being full only at the first render).
Next, in the section labled //update, you shouldn't need to do any filtering. The .data() binding you're doing to a filtered array should take care of this for you.
Also, the section labeled //create circles for the first year is unnecessary, and probably should be removed to eliminate side effect bugs. The update() function, assuming it's working fine, should take care of this for you.

D3 Collapsible Force Layout Mixing up Children and Parents

I'm working on the example located on jsfiddle, here.
It appears that I have everything structured properly, as the children are properly associated with their parents and the proper text is displayed.
The problems I've run into are as follows...
The main node (in the center) is not collapsible. The overall behavior of the graph is somewhat glitchy when compared to the example located here.
Colors do not change when nodes are collapsed, children of the parent node are displayed when the parent is collapsed. After several clicks on various nodes, children and parents seem to get switched.
My question is what section of code could be causing this and why?
Here's the code that I'm using to generate the chart. Data is missing, but is provided by the jsfiddle. Any help is appreciated, thanks in advance.
var width = 960,
height = 500,
root;
var force = d3.layout.force()
.charge(-220)
.size([width, height])
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link");
function update() {
var nodes = flatten(root);
var links = d3.layout.tree().links(nodes);
console.log(nodes);
// Restart the force layout.
force.nodes(nodes)
.links(links)
.linkDistance(55)
.start();
var link = svg.selectAll(".link")
.data(links, function(d) { return d.target.id; });
link.enter().append("line")
.attr("class", "link");
link.exit().remove();
var node = svg.selectAll("g.node")
.data(nodes)
var groups = node.enter().append("g")
.attr("class", "node")
.attr("id", function (d) {
return d.id
})
.on('click', click)
.call(force.drag);
groups.append("circle")
.attr("class","node")
.attr("x", -8)
.attr("y",-8)
.attr("r", function(d) { return d.children ? 4.5 : 10 })
.style("fill", color)
.on("click", click)
.call(force.drag);
groups.append("text")
.attr("dx", 12)
.attr("dy", "0.35em")
.style("font-size", "10px")
.style("color", "#000000")
.style("font-family", "Arial")
.text(function (d) {
console.log(d);
return d.name
});
node.exit().remove();
force.on("tick", function () {
link.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
}
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
// Color leaf nodes orange, and packages white or blue.
function color(d) {
return d._children ? "#3182bd" // collapsed package
: d.children ? "#c6dbef" // expanded package
: "#fd8d3c"; // leaf node
}
// Toggle children on click.
function click(d) {
if (!d3.event.defaultPrevented) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
Ok, there are two things going on here.
First, by default, d3 uses the index of each datum as its ID (to determine when the item enters/exits the selection). This is your issue with parent/children moving around, when the element at index X is replaced by a new element, d3 thinks they are the same. You need to provide a function to return the id:
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id; });
Second, you're only setting the fill color of the circle when an item enters the selection. You should extract the bit that is assigning the style out of the enter() block, so it will be executed each time you call update().
node.selectAll('circle').style('fill', color);
I copied and hacked together your fiddle into plunker, because jsFiddle was running really slow for me:
http://plnkr.co/edit/7AJlQub6uCGQ3VSvq4pa?p=preview

d3.js replace circle with a foreignObject

i played with this example of a force directed graph layout.
www.bl.ocks.org/GerHobbelt/3071239
or to manipulate directly, here with fiddle,
http://jsfiddle.net/BeSAb/
what i want was to replace the circle element
node = nodeg.selectAll("circle.node").data(net.nodes, nodeid);
node.exit().remove();
node.enter().append("circle")
.attr("class", function(d) { return "node" + (d.size?"":" leaf"); })
.attr("r", function(d) { return d.size ? d.size + dr : dr+1; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.style("fill", function(d) { return fill(d.group); })
.on("click", function(d) {
console.log("node click", d, arguments, this, expand[d.group]);
expand[d.group] = !expand[d.group];
init();
});
with a group (g) element that contains a svg foreignObject
node = nodeg.selectAll("g.node").data(net.nodes, nodeid);
node.exit().remove();
var nodeEnter = node.enter().append("foreignObject")
//simplified for this demo
.attr("class", function(d) { return "node" + (d.size?"":" leaf"); })
.attr('width', '22px')
.attr('height', '22px')
.attr('x', -11)
.attr('y', -11)
.append('xhtml:div')
.style("background",function(d){return fill(d.group)})
.style("width","20px")
.style("height","20px")
.style("padding","2px")
.on("click", function(d) {
console.log("node click", d, arguments, this, expand[d.group]);
expand[d.group] = !expand[d.group];
init();
});
The Graph is build correct but if i try to expand a node by clicking it, it seems that the graph isn't updated. So that all old nodes are duplicated.
i make an other Fiddle where you can show this problem by clicking a node.
http://jsfiddle.net/xkV4b/
does anyone know what i forgot, or what the issue is?
Thank you very much!
Your enter append should probably match your selection on nodeg. But even then it appears that d3 has some trouble selecting 'foreignObject' things. That may be a question/issue to bring up on the d3 google group - it may be a bug.
However you can get around it by just selecting on the class. I updated the code to read:
node = nodeg.selectAll(".fo-node").data(net.nodes, nodeid);
node.exit().remove();
var nodeEnter = node.enter().append("foreignObject")
.attr("class", function(d) { return "fo-node node" + (d.size?"":" leaf"); })
.attr('width', '22px')
...
Which seems to work.

Setting up D3 force directed graph

To the esteemed readers. I'm reasonably new in javascript and I have come across this problem. I'm trying to implement a modified version of this force directed graph:
http://mbostock.github.com/d3/ex/force.html
The json data is generated on the fly from a php script. The idea is to color all lines connecting to one specific node ( defined in a php script) in one color and all the others in shades of gray. I'm attempting to do it by matching the source variable in the json file to the variable from the php script and changing color when that is true like this:
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value);})
.style("stroke-opacity", function(d) { return d.value/10;})
.style("stroke", function(d) {
x = (tested == d.source) ? return '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.
})
however this does not work. The script works fine but without the color change if I just do this
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value);})
.style("stroke-opacity", function(d) { return d.value/10;})
.style("stroke", function(d) {
return '#707070';
})
I've been staring at this for days trying to figure out to get this done and I'm stuck. Any help would be greatly appreciated!!
Here is my complete script
<script type="text/javascript">
var width = 1200,
height = 1200;
var color = d3.scale.category20();
var tested=<?php echo $tested_source;?>; //<-- the variable from php
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("data.json", function(json) {
var force = d3.layout.force()
.charge(-130)
.linkDistance(function(d) { return 500-(50*d.value);})
.size([width, height]);
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value);})
.style("stroke-opacity", function(d) { return d.value/10;})
.style("stroke", function(d) {
x = (tested == d.source) ? return '#1f77b4' : '#707070'; //<-- Attempt to change the color of the link when this is true. But is is not working... :(
})
var node = svg.selectAll("circle.node")
.data(json.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 12)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
d.source is an object, you can't use == to determine if tested is a similar object. Have a look at this answer for more details on object equality.
If you want to test for a specific value of the d.source object described below, which I assume you want, you need to specify it.
Here is the source object architecture : (I'm using the example you pointed so the data comes from the miserables.json)
source: Object
group: 4
index: 75
name: "Brujon"
px: 865.6440689638284
py: 751.3426708796574
weight: 7
x: 865.9584580575608
y: 751.2658636251376
Now, here is the broken part in your code :
x = (tested == d.source) ? return '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.
It doesn't work because the return is misplaced.
You're mixing ternary and return statements but you don't put them in the right order :
return test ? value_if_true : value_if_false;
if you want to assign the value to x anyway, you can do
x = test ? value_if_true : value_if_false;
return x;
You should do something like this :
return (tested == d.source) ? '#1f77b4' : '#707070';// <-- Attempt to change the color of the link when this is true.
That's for the general syntax, but this won't work as is You need to pick one of the value for your test for example :
return (tested === d.source.name) ? '#1f77b4' : '#707070';
Also, if the variable from PHP is a string you should do
var tested="<?php echo $tested_source;?>"; //<-- the variable from php
and in most cases you should use json_encode to map PHP variables into javascript ones.
As a final note, I would recommend using console functions coupled with Firebug's console panel if you're using Firefox, or the Chrome Developer Tool's console panel if you're using a Chromium based browser. It would allow you to debug your code more easily.
Working code
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force().charge(-120).linkDistance(30).size([width, height]);
var svg = d3.select("#chart").append("svg").attr("width", width).attr("height", height);
var tested = 20;
d3.json("miserables.json", function (json) {
force.nodes(json.nodes).links(json.links).start();
var link = svg.selectAll("line.link")
.data(json.links)
.enter()
.append("line")
.attr("class", "link")
.style("stroke-width", function (d) {
return Math.sqrt(d.value);
}).style("stroke-opacity", function (d) {
return d.value / 10;
}).style("stroke", function (d) {
return (tested == d.source.index) ? '#ee3322' : '#707070'; //'#1f77b4'
});
var node = svg.selectAll("circle.node")
.data(json.nodes)
.enter()
.append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function (d) {
return color(d.group);
}).call(force.drag);
node.append("title").text(function (d) {
return d.name;
});
force.on("tick", function () {
link.attr("x1", function (d) {
return d.source.x;
}).attr("y1", function (d) {
return d.source.y;
}).attr("x2", function (d) {
return d.target.x;
}).attr("y2", function (d) {
return d.target.y;
});
node.attr("cx", function (d) {
return d.x;
}).attr("cy", function (d) {
return d.y;
});
});
});

Categories

Resources