简体   繁体   中英

How do I get the bounding box of a SVG text node on Firefox?

I'm trying to add a background color to my SVG element using d3 v4. In order to do that, I thought I had to get a reference to

text.node().getBBox()

so I tried the below...

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    ...

  focus.append("circle")
      .attr("r", 4.5);

  var text = focus.append("text")
      .attr("x", 9)
      .attr("dy", ".35em");
  alert(text);
  var svgrect = text.node().getBBox();
  alert(svgrect);
  var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
    rect.setAttribute("x", svgrect.x);
    rect.setAttribute("y", svgrect.y);
    rect.setAttribute("width", svgrect.width);
    rect.setAttribute("height", svgrect.height);
    rect.setAttribute("fill", "yellow");
  svg.node().insertBefore(rect, text);

but although I see the first alert, I don't see the second alert. On Firefox, I get the error

NS_ERROR_FAILURE:

without any further information. How do I get the bounding box on Firefox?

I was unable to make getBBox() (or any of the other suggested methods on the inte.net) to work with a text element, so I went into Chrome and built a character width lookup table, and then console.logged it into the console so I could copy it into my code, and use it to do a lookup against the characters in the text. It's ugly, and doesn't account for css changes, but it works if the size of your font doesn't change.

I provide it here for the poor souls who've found no other way to get the information.

How I built it the lookup table:

var clm= {}; // this is our dictionary
document.querySelector("#txt-add-title").addEventListener("keyup", function(e) {
 var bbox = document.querySelector("#measuring-stick").getBBox();
 var width = bbox.width;
 var target = e.currentTarget;
 var ch = target.value;
 if (!clm[ch]) {
     clm[ch] = width;
 }
});

Now - IN CHROME - hit each character on the keyboard once, backspace after each so you're only measuring the one character, then go back and do the Shift-characters. This will register each key and its length in your dictionary

When done, in developer console:

console.log(JSON.stringify(clm));

You'll get something like this:

{"0": 8.899147033691406, "1": 8.899147033691406, "2": 8.899147033691406, "3": 8.899147033691406, "4": 8.899147033691406,"5": 8.899147033691406, "6": 8.899147033691406, "7": 8.899147033691406, "8": 8.899147033691406, "9": 8.899147033691406, "a": 8.899147033691406, "": 0, "b": 8.899147033691406, "c": 8.181818008422852, "d": 8.899147033691406, "e": 8.899147033691406, "f": 5.454545021057129, "g": 8.899147033691406, "h": 8.899147033691406, "i": 3.5582385063171387, "j": 4.467329502105713, "k": 8.181818008422852, "l": 3.5582385063171387, "m": 13.330965042114258, "n": 8.899147033691406, "o": 8.899147033691406, "p": 8.899147033691406, "q": 8.899147033691406, "r": 5.909090518951416, "s": 8.004261016845703, "t": 4.545454502105713, "u": 8.899147033691406, "v": 8.181818008422852, "w": 12.272727012634277, "x": 8.63636302947998, "y": 8.181818008422852, "z": 8.181818008422852, "A": 11.363636016845703, "B": 10.674715995788574, "C": 11.555397033691406, "D": 11.555397033691406, "E": 10.674715995788574, "F": 9.779829025268555, "G": 12.450284004211426, "H": 11.555397033691406, "I": 4.4460225105285645, "J": 8.004261016845703, "K": 10.909090042114258, "L": 8.899147033691406, "M": 13.330965042114258, "N": 11.555397033691406, "O": 12.450284004211426, "P": 10.674715995788574, "Q": 12.450284004211426, "R": 11.818181037902832, "S": 10.674715995788574, "T": 9.779829025268555, "U": 11.555397033691406, "V": 11.363636016845703, "W": 15.106534004211426, "X": 11.363636016845703, "Y": 11.363636016845703, "Z": 9.779829025268555, "`": 5.33380651473999, "-": 5.33380651473999,"=": 9.346590995788574, "~": 9.346590995788574, "!": 4.4460225105285645, "@": 16.242897033691406, "#": 9.090909004211426, "$": 8.899147033691406, "%": 14.232954025268555, "^": 7.514204502105713, "&": 10.674715995788574, "*": 6.228693008422852, "(": 5.33380651473999, ")": 5.33380651473999, "_": 10, "+": 9.346590995788574, ",": 4.4460225105285645, "<": 9.346590995788574, ".": 4.4460225105285645, ">": 9.346590995788574, "/": 5, "?": 8.899147033691406, ":": 4.4460225105285645, ";": 4.4460225105285645,"'": 3.061079502105713, "[": 4.545454502105713, "{": 5.454545021057129, "]": 4.4460225105285645, "}": 5.348011016845703},
   

Now assign that to a variable and whenever you need to get the length of that text node, you just get the value and loop through the characters to get the length, like this:

var width = 0;
 var bbox = document.querySelector("#measuring-stick").getBBox();
if (bbox && bbox.width > 0) { // chrome works, yay!
                    width = bbox.width;
                } else { // mozilla needs the hack
                    var v= <get your element text here > ;
                    if (v.length > 0) {
                        for (let vp = 0; vp < v.length; vp++) {
                            width += mozCharLenLookup[v[vp]] || 0;
                        }
                    }
                }

This solution killed me a little inside, but it works when the font sizes don't change, and they're roughly the same size in both browsers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM