Reputation: 395
I am aware that SVG elements have no z index. I am also aware that the order at which you define an SVG element will determine its vertical ordering. A fix to this from what I have seen on similar questions is typically to select the individual element and do something like: identifier.node().parentNode.appendChild(identifier.node());
Which brings it to the top of the deck. However, this doesn't work when you have multiple SVG elements that need to be moved to the top.
The following code (also see codepen link at bottom) is a boiled down example that consists of two adjacent circles and a square below them. Each circle has mouseover events that render their colors differently. However, the leftmost circle has text labels that, at the moment have an opacity of 0. The opacity changes when you hover over the square. These text labels obstruct the mouseover event of the circles when hovering over their respective positions. Crucially, while I could sloppily change the color of the leftmost circle when hovering over the text, some text labels overlap with the rightmost circle and would therefore render the circle an incorrect color at these overlapping positions. The solution I am looking for would change the vertical ordering of all text labels so that when hovering over all points of the leftmost circle, the circle turns green, and likewise, when hovering over the square, all text labels are on top, in red. How do I select all of them?
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Adjusted radii</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.2.0/d3.min.js" type="text/javascript"></script>
<style type="text/css">
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 500;
var padding = 20;
var circleDataset1 = [
[800, 1000]
];
var circleDataset2 = [
[900, 500]
];
var squareDataset = [
[800, 1200]
];
var labs = [[100,450],[200,450], [100,550],[200,650],[50,450], [200,750],[0,650],[200,550],[250,450], [60,650],[250,550],[0,550], [0,750], [50,750], [100,750],]
//Create scale functions
var xScale = d3.scale.linear()
.domain([0, d3.max(circleDataset1, function(d) { return d[0]; })])
.range([padding, w - padding * 2]);
var yScale = d3.scale.linear()
.domain([0, d3.max(circleDataset1, function(d) { return d[1]; })])
.range([h - padding, padding]);
var rScale = d3.scale.linear()
.domain([0, 2000 ])
.range([0.05, 25]);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var svg2 = d3.select('body')
.append("svg")
.attr("width", w)
.attr("height", h);
//Create circles
svg.selectAll("circle")
.data(circleDataset1)
.enter()
.append("circle")
.attr("cx", 100)
.attr("cy", 200)
.attr("r", 100)
.on('mouseover',function(d) {
d3.select(this)
.attr('fill','green');
})
.on("mouseout", function(d) {
d3.select(this)
.attr('fill','black');
})
svg.selectAll("ellipse")
.data(circleDataset2)
.enter()
.append("ellipse")
.attr("cx", 200)
.attr("cy", 200)
.attr("rx", 100)
.attr("ry", 100)
.attr('fill','grey')
.on('mouseover',function(d) {
d3.select(this)
.attr('fill','blue');
})
.on("mouseout", function(d) {
d3.select(this)
.attr('fill','grey');
})
svg.selectAll('rect')
.data(squareDataset)
.enter()
.append("rect")
.attr("x", 100)
.attr("y", 400)
.attr("width", 100)
.attr('height',500)
.attr('fill','red')
.on('mouseover',function(d) {
d3.select(this)
d3.selectAll('.textlabel').style({opacity:'1'});
})
.on('mouseout',function(d) {
d3.select(this)
d3.selectAll('.textlabel').style({opacity:'0'});
})
//Create labels
svg.selectAll("text")
.data(labs)
.enter()
.append("text")
.text(function(d) {
return d[0] + "," + d[1];
})
.attr("x", function(d) {
return xScale(d[0]);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "red")
.attr('class','textlabel')
.style('opacity',0)
//////////////////////
// Even if I select all elements by a classname and , only 1 of the elements will move to the front
// textLabs = d3.selectAll('.textlabel')
// .style('opacity',1.0)
// textLabs.node().parentNode.appendChild(textLabs.node());
///////////////////////
</script>
</body>
</html>
https://codepen.io/robfran4566/pen/VEpPRB
Upvotes: 2
Views: 1275
Reputation: 21821
No Javascript needed for anything you want to do.
pointer-events: none
on the text labelscircle {
fill: black;
}
circle:hover {
fill: green;
}
ellipse {
fill: grey;
}
ellipse:hover {
fill: blue;
}
rect {
fill: red;
}
g.textlabel {
font-family: sans-serif;
font-size: 11px;
fill: red;
opacity: 0;
pointer-events: none;
}
rect:hover + g {
opacity: 1;
}
<svg width="500" height="500">
<circle cx="100" cy="200" r="100" fill="black"></circle>
<ellipse cx="200" cy="200" rx="100" ry="100" fill="grey"></ellipse>
<rect x="100" y="400" width="100" height="500" fill="red"></rect>
<g class="textlabel">
<text x="75" y="273">100,450</text>
<text x="130" y="273">200,450</text>
<text x="75" y="227">100,550</text>
<text x="130" y="181">200,650</text>
<text x="47.5" y="273">50,450</text>
<text x="130" y="135">200,750</text>
<text x="20" y="181">0,650</text>
<text x="130" y="227">200,550</text>
<text x="157.5" y="273">250,450</text>
<text x="53" y="181">60,650</text>
<text x="157.5" y="227">250,550</text>
<text x="20" y="227">0,550</text>
<text x="20" y="135">0,750</text>
<text x="47.5">50,750</text>
<text x="75" y="135">100,750</text>
</g>
</svg>
Upvotes: 3