Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rectangle border around SVG text

Tags:

css

svg

Trying to put a border around some SVG text, and I am getting varying results.

HTML: (with the mute class)

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <text x="37.5" y="37.5" class="ablate-x mute">X</text>
</svg>

CSS:

.ablate-x {
   font-size: 24px;
   color: gray;
   opacity: 0.5;
   font-weight: 900;
   cursor: hand;
   cursor: pointer;
}

.mute {
   opacity: 1;
   fill: red;
   stroke: red;
   stroke-width: 2px;
   /* we tried border: 2px solid red;   but it didn't work */
}

D3.js:

.on("click", function(d) {
    var selection = d3.select(this);
    selection.classed("mute", (selection.classed("mute") ? false : true));
 })

Here we have the X without the mute class grey

Here we have the X with the mute class red but without the border

This is what we are trying to get the border to look like red with border

Note: its parent is a group, not an HTML element.

JS Fiddle: http://jsfiddle.net/chrisfrisina/yuRyr/5/

like image 907
chris Frisina Avatar asked Jun 20 '13 15:06

chris Frisina


3 Answers

With newer versions of d3.js, there's no interpolate() function on the line generator (reference: "Interpolate" is not a function), so I somewhat modified Alvin K's answer as shown in the code below (Typescript). In particular, the code is used to simulate a hover effect on an SVG text.

    const group = d3.select(/* your node */)
    group
        .append("rect")
        .attr("stroke", "none")
        .attr("fill", "none")

    group
        .append("text")
        .call((node) => {
            node.on("mouseenter", function(this: any) {
                const rect = this.getBBox()
                d3.select(this.parentNode)
                    .select("rect")
                    .attr("x", rect.x - offset)
                    .attr("y", rect.y - offset)
                    .attr("width", rect.width + offset * 2)
                    .attr("height", rect.height + offset * 2)
                    .style("stroke-width", 1)
                    .style("stroke", "black")
                    .style("stroke-dasharray", "4")
                    .style("fill", "none");
                })
            node.on("mouseleave", function(this: any) {
                d3.select(this.parentNode)
                    .select("rect")
                    .style("stroke", "none")
            })
        })

PS: it's not necessary to use call() on the selection, you could just call on() directly, however in my actual code, I refactored the callback into a class method.

var offset = 5
onload();

function onload() {
    var svg = d3.select("body")
                .append("svg")
                .attr("width", 200)
                .attr("height", 200)

    const group = svg
        .append("g")
        .attr("transform", "translate(10, 40)")
    group
        .append("rect")
        .attr("stroke", "none")
        .attr("fill", "none")

    group
        .append("text")
        .attr("fill", "black")
        .attr("font-weight", 600)
        .attr("font-size", "16px")
        .text("HOVER ON ME")
        .call((node) => {
            node.on("mouseenter", function() {
                const rect = this.getBBox()
                d3.select(this.parentNode)
                    .select("rect")
                    .attr("x", rect.x - offset)
                    .attr("y", rect.y - offset)
                    .attr("width", rect.width + offset * 2)
                    .attr("height", rect.height + offset * 2)
                    .style("stroke-width", 1)
                    .style("stroke", "black")
                    .style("stroke-dasharray", "4")
                    .style("fill", "none");
                })
            node.on("mouseleave", function() {
                d3.select(this.parentNode)
                    .select("rect")
                    .style("stroke", "none")
            })
        })
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>
like image 199
blackgreen Avatar answered Jan 16 '23 16:01

blackgreen


SVG elements don't support the CSS border property as you have discovered. Your options are

  1. Draw a red <rect> around the text as a border
  2. Put a border on the outer <svg> element if its parent is a html element. The outer <svg> element is a replaced element and will support the CSS border property.
like image 28
Robert Longson Avatar answered Jan 16 '23 15:01

Robert Longson


To draw a rect around text on click, update the code to:

var svgcanvas = d3.select('svg');

$("#xc").on("click", function (d) {
    var selection = d3.select(this);
    var rect = this.getBBox();
    var offset = 2; // enlarge rect box 2 px on left & right side
    selection.classed("mute", (selection.classed("mute") ? false : true));

    pathinfo = [
        {x: rect.x-offset, y: rect.y }, 
        {x: rect.x+offset + rect.width, y: rect.y}, 
        {x: rect.x+offset + rect.width, y: rect.y + rect.height }, 
        {x: rect.x-offset, y: rect.y + rect.height},
        {x: rect.x-offset, y: rect.y },
    ];

    // Specify the function for generating path data
    var d3line = d3.svg.line()
        .x(function(d){return d.x;})
        .y(function(d){return d.y;})
        .interpolate("linear"); 
    // Draw the line
    svgcanvas.append("svg:path")
        .attr("d", d3line(pathinfo))
        .style("stroke-width", 1)
        .style("stroke", "red")
        .style("fill", "none");

})

On this html:

    <!DOCTYPE html>
    <html>
    <head>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    </head>
    <body>
        <svg width="720" height="720">
            <text x="37.5" y="37.5" id="xc">X</text>
        </svg>
    </body>
    </html>
like image 35
Alvin K. Avatar answered Jan 16 '23 17:01

Alvin K.