SVG rect border, not stroke - javascript

I am making a d3 graph and trying to put a border around my rect elements. The rect elements are appended to a cell and the text elements are appended to the same cell. Thus if I change the stroke in the rect I lose all the text for some reason, and if I change the stroke in the cell the borders and fonts change too.
This is a portion of my code for drawing the graph.
this.svg = d3.select("#body").append("div")
.attr("class", "chart")
.style("position", "relative")
.style("width", (this.w +this.marginTree.left+this.marginTree.right) + "px")
.style("height", (this.h + this.marginTree.top + this.marginTree.bottom) + "px")
.style("left", this.marginTree.left +"px")
.style("top", this.marginTree.top + "px")
.append("svg:svg")
.attr("width", this.w)
.attr("height", this.h)
.append("svg:g")
.attr("transform", "translate(.5,.5)");
this.node = this.root = this.nestedJson;
var nodes = this.treemap.nodes(this.root)
.filter(function(d) { return !d.children; });
this.tip = d3.tip()
.attr('class', 'd3-tip')
.html(function(d) {
return "<span style='color:white'>" + (d.name+",\n "+d.size) + "</span>";
})
this.svg.call(this.tip);
var cell = this.svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.call(this.position)
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return this.zoom(this.node == d.parent ? this.root : d.parent); })
.style("border",'black');
var borderPath = this.svg.append("rect")
.attr("x", this.marginTree.left)
.attr("y", this.marginTree.top)
.attr("height", this.h - this.marginTree.top - this.marginTree.bottom )
.attr("width", this.w - this.marginTree.left - this.marginTree.right)
.style("stroke", 'darkgrey')
.style("fill", "none")
.style("stroke-width", '3px');
cell.append("svg:rect")
.attr("id", function(d,i) { return "rect-" + (i+1); })
.attr("class","highlighting2")
.attr("title", function(d) {return (d.name+", "+d.size);})
.attr("data-original-title", function(d) {return (d.name+",\n "+d.size);})
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy ; })
.on('mouseover', this.tip.show)
.on('mouseout', this.tip.hide)
.style("fill", function(d) {return coloring(d.color);});
cell.append("svg:text")
.attr("class", "treemap-text nameTexts")
.attr("id", function(d,i) { return "name-" + (i+1); })
.attr("x", cellMargin)
.attr("y", function(d) { return parseInt($('.treemap-text').css('font-size'))+cellMargin; })
.text(function(d) {return (d.name);});
cell.append("svg:text")
.attr("class", "treemap-text sizeTexts")
.attr("id", function(d,i) { return "size-" + (i+1); })
.attr("x", cellMargin)
.attr("y", function(d) { return 2*parseInt($('.treemap-text').css('font-size'))+2*cellMargin; })
.text(function(d) {return (d.size);});
Additionally, I thought about creating lines and drawing four lines around each rect element, but was wondering if there is an easier way. Thanks.

I didn't check fully through your source, it would also be helpful to work with jsbin, codepen, jsfiddle or other online platforms to show your problem.
Actually I think you just have misinterpreted the SVG presentation attributes and their styling with CSS. For SVG elements only SVG presentation attributes are valid in CSS. This means there is no border property as you have it in your code. Also note that for <text> elements the fill color is the font-body color and the stroke is the outline of the font. Consider that stroke and fill are inherited down to child element which means that if you have a rectangle with a stroke style and some containing text element that they will have the stroke applied as outline and you'd need to override the styles there.
Hope you can solve your issue.
Cheers
Gion

Related

Tooltip is not showing at appended 'rect' on top of point/circle in d3 chart

I have a d3 line chart with a tooltip, I am facing a problem with a tooltip.
I have functionality, on click of points/circle I am appending rect to g, which is adding on top of the existing rect which has the tooltip functionality.
My tooltip is not coming at selected(rect) Graph Point.
g.append("rect")
.attr("class", "overlay")
.attr("id", "firstLayer")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {
focus.style("display", null);
div
.transition()
.duration(200)
.style("opacity", 0.9);
})
.on("click", function(d, index) {
let newXScale = newX ? newX : xScale;
if (rect) rect.remove();
rect = g
.append("rect")
.attr("x", newXScale(d.startTime) - 12.5)
.attr("y", 0)
.attr("width", 24)
.attr("height", height + 5)
.attr("data", d.startTime)
.style("fill", "steelblue")
.style("opacity", 0.5);
if (clickLine) clickLine.remove();
clickLine = g
.append("line")
.attr("x1", newXScale(d.startTime))
.attr("y1", yScale(yDomain[0]))
.attr("x2", newXScale(d.startTime))
.attr("y2", yScale(yDomain[1]))
.attr("class", "focusLine")
.style("opacity", 0.5);
})
rect element is coming on top of the gm on hover of that tooltip is not coming, any suggestions on how to fix it ?
On mouse hover -
At selected Graph Point -
CodeSandbox link below -
https://codesandbox.io/s/damp-dawn-82hxc
Please guide me what can be changed.
on click of the circle you are appending another rectangle to g, which is adding on top of the existing rect which has the tool tip functionality
Note: d3 js adds layer/shape on top of another which basically overrides the existing layer/shape functionality with the new layer/shape if they are in the same position
To avoid that we have to draw the layers depends on their intended purpose and position.
Solution for the above problem
append background rects for circle you want to create with opacity: 0
g.selectAll(".faaa")
.data(data)
.enter()
.append("rect")
.attr("class", "faaa")
.attr("id", d => "rect_" + d.id)
.attr("x", d => xScale(d.startTime) - 12.5)
.attr("y", 0)
.attr("width", 24)
.attr("height", height + 5)
.attr("data", d => d)
.style("fill", "steelblue")
.style("opacity", 0);
append firstLayer rect which has the tooltip functionality so the background rect won't break the tooltip functionality
g.append("rect")
.attr("class", "overlay")
.attr("id", "firstLayer")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {
focus.style("display", null);
div
.transition()
.duration(200)
.style("opacity", 0.9);
})
.on("mouseout", function() {
focus.style("display", "none");
div
.transition()
.duration(300)
.style("opacity", 0);
})
.on("mousemove", function() {
var mouse = d3.mouse(this);
var mouseDate = xScale.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
let d;
// work out which date value is closest to the mouse
if (typeof d1 !== "undefined") {
d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
} else {
d = d0;
}
div
.html(
`<span>${parseDate(d.startTime)}</span>
<span> Changes : ${d.magnitude} % </span>`
)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
var x = xScale(d.startTime);
var y = yScale(d.magnitude);
focus
.select("#focusCircle")
.attr("cx", x)
.attr("cy", y);
focus
.select("#focusLineX")
.attr("x1", x)
.attr("y1", yScale(yDomain[0]))
.attr("x2", x)
.attr("y2", yScale(yDomain[1]));
focus
.select("#focusLineY")
.attr("x1", xScale(xDomain[0]))
.attr("y1", y)
.attr("x2", xScale(xDomain[1]))
.attr("y2", y);
});
append circle and add click functionality then change the opacity to highlight the background rect
g.selectAll(".foo")
.data(data)
.enter()
.append("circle")
.attr("id", d => d.id)
.attr("class", "foo")
.attr("data", d => d)
.attr("cx", function(d) {
return xScale(d.startTime);
})
.attr("cy", function(d) {
return yScale(d.magnitude);
})
.attr("r", function(d) {
return 6;
})
.on("click", function(d) {
// change the opacity here
d3.select("#rect_" + d.id).style("opacity", 0.5);
})
.attr("class", "circle");
Hope this solves the above problem...

D3 position x axis label within rectangle and rotate 90 degrees

I am using D3 to create a basic bar graph
For my x-axis, I want to position each label above their respective bar. The text should also be rotated 90 degrees
To see the code that does this, start at line 51. https://codepen.io/Fallenstedt/pen/xdYooE
//This is where I attempt to create an x Axis label
//create a container to hold the text element
var textContainer = svg.append('g')
.selectAll('g')
.data(data).enter()
.append('g')
.attr('class', 'x-axis')
.attr('x', function(d, i) {
return i * (width/data.length)
})
.attr('y', function(d, i) {
return height - (d.value) + 15;
})
.attr("transform", function(d, i) {return "translate(" + (i * (width/data.length)) + ",330)";});
//now that a container is made, I can append a text element to it so I can rotate the text 90 degrees.
textContainer.append('text')
.text(function(d) {
return d.type
})
.attr('font-size', '34px')
.attr('fill', 'white')
.attr("text-anchor","end")
.attr("transform", function(d, i) {return "translate(40,0) rotate(-90,0,0)";});
The labels appear and they are rotated 90 degrees, however I cannot position them to be above their respective rectangle. How can I position each x-axis label to be directly above their rectangle? I feel that my approach to this is overly complicated.
You can create the rect and text elements inside the same container, e.g.
var rContainer = svg
.selectAll(".bar")
.data(data)
.enter()
.append("g");
//append rectangles for the bar chart
rContainer
.append("rect")
.attr("class", "bar")
.style("fill", function(d, i) { return color(i); })
.attr("x", function(d) { return x(d.type); })
.attr("y", 0)
.attr("width", x.bandwidth())
.attr("height", 0)
.transition()
.duration(500) //length of animation
.delay(function(d, i) { return i * 100; }) //delay must be less than duration
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
//append a text element to it so I can rotate the text 270 degrees.
rContainer
.append("text")
.text(function(d) { return d.type; })
.attr("width", x.bandwidth())
.attr("font-size", "34px")
.attr("fill", "white")
.attr("text-anchor", "start")
.attr("transform", function(d, i) {
// http://stackoverflow.com/questions/11252753/rotate-x-axis-text-in-d3
var yVal = y(d.value) - 6;
var xVal = x(d.type) + x.bandwidth() / 1.6;
return "translate(" + xVal + "," + yVal + ") rotate(270)";
});
You can check this working demo // starts in line 40

Dynamically create scrollable SVG

I am trying to create a scrollable list. I have looked at other tutorials on here but it does not seem to be working. Essentially I am appending an SVG to a div. Inside this SVG is a D3JS stacked Bar Graph. to the right of this bar graph I am appending a 'g' element with an svg inside. I have set a height for this right SVG. Inside this I have populated a list that would extend beyond the height of the SVG. I have set the CSS for this svg to 'overflow-y: scroll'.
In spite of all of this I can not get this svg to scroll. Instead it just grows to the size of the list and extends past to intended bounds. Please See code below.
var barSVG = d3.select("#documents_reviewed_bar_chart").append("svg")
.classed('barChart-docs-reviewed', true)
.attr('id', 'barSVG')
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('id', 'gElement')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var count = 0;
var graph = barSVG.append('g')
.attr('id', 'graphElement')
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Date"; }));
data.forEach(function(d) {
var myDate = d.Date; //add to stock code
var y0 = 0;
d.people = color.domain().map(function(name) { return {myDate:myDate, name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.people[d.people.length - 1].y1;
count = isNaN(d.total) ? count : count + d.total
});
x.domain(data.map(function(d) { return d.Date; }));
y.domain([0, d3.max(data, function(d) { return d.total; })]);
graph.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)" )
.style("cursor", "pointer")
.on('click', renderHorizontalChart);
graph.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
//.text("Population");
graph.append('text')
.text('Total: ' + count)
.attr('x', 20)
.attr('y', -10)
var state = graph.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + "0" + ",0)"; });
//.attr("transform", function(d) { return "translate(" + x(d.Date) + ",0)"; })
state.selectAll("rect")
.data(function(d) {
return d.people;
})
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", height)
.attr("x",function(d) { //add to stock code
return x(d.myDate)
})
.attr("height", 0 )
.style("fill", function(d) { return color(d.name); })
.transition()
.duration(1000)
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.attr("class", function(d) {
classLabel = d.name.replace(/,\s/g, ''); //remove spaces
return "class" + classLabel;
});
state.selectAll("rect")
.on("mouseover", function(d){
var delta = d.y1 - d.y0;
var xPos = parseFloat(d3.select(this).attr("x"));
var yPos = parseFloat(d3.select(this).attr("y"));
var height = parseFloat(d3.select(this).attr("height"))
d3.select(this).attr("stroke","black").attr("stroke-width",2);
tooltip.style("visibility", "visible");
tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
tooltip.style('background', 'black')
tooltip.style('color', 'white')
tooltip.style('border-radius', '3px')
tooltip.style('padding', '5px')
tooltip.style('opacity', '0.8')
tooltip.style('font-size', '10px;')
tooltip.text(d.name +": "+ delta)
})
.on("mouseout",function(){
tooltip.style("visibility", "hidden");
graph.select(".tooltip").remove();
d3.select(this).attr("stroke","pink").attr("stroke-width",0.2);
})
var itemsAmount = 0
var rightSVG = barSVG.append('svg').classed('rightSVG', true)
.attr('height', '390')
.attr('id', 'rightSVG')
var legendSVG = rightSVG.append('svg').classed('legendSVG', true)
.attr('id', 'legendSVG')
var legend = legendSVG.selectAll(".legend")
.data(color.domain().slice().reverse())
.enter().append("g")
//.attr("class", "legend")
.attr("class", function (d) {
itemsAmount = itemsAmount + 1
legendClassArray.push(d.replace(/,\s/g, '')); //remove spaces
return "legend";
})
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
//reverse order to match order in which bars are stacked
legendClassArray = legendClassArray.reverse();
legend.append("rect")
.attr("x", width - 0)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.attr("id", function (d, i) {
return "id#" + d.replace(/,\s/g, '');
})
.on("mouseover",function(){
if (active_link === "0") d3.select(this).style("cursor", "pointer");
else {
if (active_link.split("class").pop() === this.id.split("id#").pop()) {
d3.select(this).style("cursor", "pointer");
} else d3.select(this).style("cursor", "auto");
}
})
.on("click",function(d){
if (!this.id.includes('active')) { //nothing selected, turn on this selection
d3.select(this)
.attr('id', function(){
return this.id + 'active'
})
.style("stroke", "black")
.style("stroke-width", 2);
active_link = this.id.split("id#").pop();
plotSingle(this);
} else { //deactivate
d3.select(this)
.classed("active", false)
.attr('id', function() {
return this.id.replace('active', '')
})
.style("stroke", "none")
.style("stroke-width", 0);
plotSingle(this);
}
});
legend.append("text")
.attr("x", width - 6)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Run Query')
.on('click', function(){
if (newArr.length > 0) {
d3.select('#barSVG').remove();
runScript(newArr)
}
});
legendSVG.append("text")
.classed('queryButton', true)
.attr("x", width - 6)
.attr("y", height + 18)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text('Reset')
The Specific SVG that I want to be scrollable is the 'rightSVG'.
As you can see in the image, the names are cut off. There should be a scrollable legend where I am able to see 29 data items.
Also, I have added the below CSS:
#documents_reviewed_bar_chart, #gElement{
max-height: 390;
overflow-y: scroll;
}
Short answer: you can't have a scrollable SVG inside another SVG. The overflow-y: scroll applies to HTML elements, not to SVG elements.
Alternative (and hacky) answer: technically, what you want is possible, but you'll have to wrap your inner SVG in an HTML element, which has to be inside a foreignObject.
This alternative is suboptimal, makes little sense and doesn't work on IE. However, just for the sake of curiosity, this is how you can do it:
var outerSvg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 200)
.style("background-color", "darkkhaki");
var foreign = outerSvg.append("foreignObject")
.attr("x", 300)
.attr("y", 10)
.attr("width", 150)
.attr("height", 180)
.append("xhtml:div")
.style("max-height", "180px")
.style("overflow-y", "scroll");
var innerSvg = foreign.append("svg")
.attr("width", 133)
.attr("height", 1000)
.style("background-color", "powderblue");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var texts = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("text")
.attr("x", 40)
.attr("y", (d,i)=> 20 + 15*i)
.text("foo bar baz")
var rects = innerSvg.selectAll("foo")
.data(d3.range(65))
.enter()
.append("rect")
.attr("x", 10)
.attr("y", (d,i)=> 8 + 15*i)
.attr("width", 20)
.attr("height", 13)
.attr("fill", (d,i)=>color(i));
<script src="https://d3js.org/d3.v4.min.js"></script>
The outer SVG is light brown (or khaki). The inner SVG, at the right, is blue, and it's inside a <div> with overflow-y: scroll;.

how to change stack order of text label in JavaScript?

I am trying to plot a network graph using networkD3 in R. I wanted to make some changes to the display so that the text labels (which appears when mouseover) can be easily read.
Please refer to the link here for an example. Note: Jump to the d3ForceNetwork plot.
As seen in the example, the labels are hard to read due to its colour and it often gets obstructed by the surrounding nodes. I have been messing around with the JS file and managed to change the text label color to black. However, having no knowledge of JS or CSS (I can't even tell the difference between the 2 actually), I have no idea how I can change the stack order such that the text labels will always be displayed above any other objects.
Can anyone advise me on how I can achieve the desired outcome?
Below is the full JS file:
HTMLWidgets.widget({
name: "forceNetwork",
type: "output",
initialize: function(el, width, height) {
d3.select(el).append("svg")
.attr("width", width)
.attr("height", height);
return d3.layout.force();
},
resize: function(el, width, height, force) {
d3.select(el).select("svg")
.attr("width", width)
.attr("height", height);
force.size([width, height]).resume();
},
renderValue: function(el, x, force) {
// Compute the node radius using the javascript math expression specified
function nodeSize(d) {
if(options.nodesize){
return eval(options.radiusCalculation);
}else{
return 6}
}
// alias options
var options = x.options;
// convert links and nodes data frames to d3 friendly format
var links = HTMLWidgets.dataframeToD3(x.links);
var nodes = HTMLWidgets.dataframeToD3(x.nodes);
// get the width and height
var width = el.offsetWidth;
var height = el.offsetHeight;
var color = eval(options.colourScale);
// set this up even if zoom = F
var zoom = d3.behavior.zoom();
// create d3 force layout
force
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(options.linkDistance)
.charge(options.charge)
.on("tick", tick)
.start();
// thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview
// http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed
var drag = force.drag()
.on("dragstart", dragstart)
// allow force drag to work with pan/zoom drag
function dragstart(d) {
d3.event.sourceEvent.preventDefault();
d3.event.sourceEvent.stopPropagation();
}
// select the svg element and remove existing children
var svg = d3.select(el).select("svg");
svg.selectAll("*").remove();
// add two g layers; the first will be zoom target if zoom = T
// fine to have two g layers even if zoom = F
svg = svg
.append("g").attr("class","zoom-layer")
.append("g")
// add zooming if requested
if (options.zoom) {
function redraw() {
d3.select(el).select(".zoom-layer").attr("transform",
"translate(" + d3.event.translate + ")"+
" scale(" + d3.event.scale + ")");
}
zoom.on("zoom", redraw)
d3.select(el).select("svg")
.attr("pointer-events", "all")
.call(zoom);
} else {
zoom.on("zoom", null);
}
// draw links
var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) { return d.colour ; })
//.style("stroke", options.linkColour)
.style("opacity", options.opacity)
.style("stroke-width", eval("(" + options.linkWidth + ")"))
.on("mouseover", function(d) {
d3.select(this)
.style("opacity", 1);
})
.on("mouseout", function(d) {
d3.select(this)
.style("opacity", options.opacity);
});
// draw nodes
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.style("fill", function(d) { return color(d.group); })
.style("opacity", options.opacity)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.call(force.drag);
node.append("circle")
.attr("r", function(d){return nodeSize(d);})
.style("stroke", "#fff")
.style("opacity", options.opacity)
.style("stroke-width", "1.5px");
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name })
.style("font", options.fontSize + "px " + options.fontFamily)
.style("opacity", options.opacityNoHover)
.style("pointer-events", "none");
function tick() {
node.attr("transform", function(d) {
if(options.bounded){ // adds bounding box
d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x));
d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y));
}
return "translate(" + d.x + "," + d.y + ")"});
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; });
}
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style('position','relative')
.style("opacity", 1);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d);});
d3.select(this).select("text").transition()
.duration(1250)
.attr("x", 0)
.style("font", options.fontSize + "px ")
.style("opacity", options.opacityNoHover);
}
function click(d) {
return eval(options.clickAction)
}
// add legend option
if(options.legend){
var legendRectSize = 18;
var legendSpacing = 4;
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = legendRectSize;
var vert = i * height+4;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.style('fill', 'darkOrange')
.text(function(d) { return d; });
}
// make font-family consistent across all elements
d3.select(el).selectAll('text').style('font-family', options.fontFamily);
},
});
I suspect I need to make some changes to the code over here:
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style("opacity", 1);
}
You need to resort the node groups holding the circles and text so the currently mouseover'ed one is the last in that group, and thus the last one drawn so it appears on top of the others. See the first answer here -->
Updating SVG Element Z-Index With D3
In your case, if your data doesn't have an id field you may have to use 'name' instead as below (adapted to use the mouseover function you've got):
function mouseover(d) {
d3.selectAll("g.node").sort(function (a, b) {
if (a.name != d.name) return -1; // a is not the hovered element, send "a" to the back
else return 1; // a is the hovered element, bring "a" to the front (by making it last)
});
// your code continues
The pain might be that you have to do this edit for every d3 graph generated by this R script, unless you can edit the R code/package itself. (or you could suggest it to the package author as an enhancement.)

Overlapping legend d3.js

I am creating the legends with triangle shapes. One is "Yes", the other one is "No". By running the code below, it generate two triangles but they are overlapping. I am trying to seperate them by using this line of code .attr("y", function(d,i) {return 50+i*40;}) but seems like it doesn't work.
Can anyone tell me how to fix it? Thanks!
Click here! This is an html sreenshot for this part of script
var legendname = ["Yes","No"];
var legend = svg.selectAll(".legend")
.data(legendname)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (m.t - 30) + ")";
});
legend.append("path")
.attr("d", d3.svg.symbol().type("triangle-up").size(128))
*** .attr("y", function(d,i) {return 50+i*40;})
.style('fill', function(d) {return color(d);});
legend.append("text")
.attr("y", function(d,i) {return 50+i*20;})
.attr("x", 30)
.text(function(d) { return d; })
You will have to update the translate y attribute of groups instead of the paths. And also there is no need for extra calculations for y attributes of texts and paths then.
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (30+i*40) + ")";
});
Working Code Snippet:
var w=40; //Sample chart width
var color = d3.scale.category20c();
var svg = d3.select("body").append("svg").attr({ height: 500, width: 400 });
var legendname = ["Yes", "No"];
var legend = svg.selectAll(".legend")
.data(legendname)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(" + (w + 150) + "," + (30+i*40) + ")";
});
legend.append("path")
.attr("d", d3.svg.symbol().type("triangle-up").size(128))
.style('fill', function(d,i) {
return color(i);
});
legend.append("text")
.attr("dx",10)
.attr("dy",".4em")
.text(function(d) {
return d;
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Categories