Reputation: 5402
I want to add a checkbox with a label to my d3 svg. I'd like them to be in separate g
elements because for some reason, the font rendering doesn't match the rest of the svg if the label is in the foreignObject (irrelevant to my question, although I'd be happy with an explanation there to that too).
However, I can't figure out how to get them to align vertically. Yes, I could introduce some fudge factor, but it seems like that would only align for one particular size of the svg, which can change. How do I do it?
let y=40;
svg.append("g")
.append("foreignObject")
.attr("x", "4ex")
.attr("y", y)
.attr("width", 75)
.attr("height", 25)
.append("xhtml:body")
.html("<form><input type=checkbox id=check/>text1</form>");
//seems I must render this outside the foreignObject in order to match font rendering of the svg
svg.append("g")
.append("text")
.attr("y", y)
.attr("height", 25)
.attr("x", "7ex")
.text("text2")
That is, I'd like the text to line up with the checkbox as if the text had been in the foreignObject
's html. It seems like I should be able to just set the same value of y
for both.
http://jsfiddle.net/48rdrp6a/2/
If it's a matter of setting some vertical-align
, I can't figure out where to put it.
Upvotes: 1
Views: 4318
Reputation: 2145
The main problem is that the y
attribute on the foreignObject
specifies the top of the box, while the y
attribute on the text
specifies the baseline of the text.
In the given example the foreignObject
will therefore start at 40 and grow downwards to 65, while the text
will have its baseline start at 40 with the ascends and descends going upwards and downwards from 40 respectively.
This behaviour can be changed through the alignment-baseline
property. Using before-edge
moves the EM box so that y
specifies the top edge for the text
as well, making both elements compatible.
Additional to that fundamental difference there is a problem with browser default styling. The body
in the foreignObject
will have a browser specific default styling, which may offset the form and checkbox, bringing it out of alignment again. In Chrome this for example adds 8px of margin.
var svg = d3.select("#chart").append("svg")
.attr("width", 200)
.attr("height", 200);
let y = 40;
svg.append("g")
.append("foreignObject")
.attr("x", "4ex")
.attr("y", y)
.attr("width", 75)
.attr("height", 25)
.append("xhtml:body")
.html("<form><input type=checkbox id=check/></form>");
svg.append("g")
.append("text")
.attr("y", y)
.attr("height", 25)
.attr("x", "6ex")
.text("text2")
// svg repositioning
$("svg").css({
top: 200,
left: 200,
position: 'absolute'
});
#chart {
border: 1px solid green;
}
svg {
background: #AABBFF;
border: 2px solid #8899EE;
}
text {
alignment-baseline: before-edge;
}
foreignObject body {
all: unset;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
I have used CSS to set the alignment-baseline
, but using the corresponding attribute would have worked as well.
For counteracting the body
default styling I've used a combination of all: unset
to remove all browser styling and then restoring display: block
.
Upvotes: 3