Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

208
Vistas
Precise width of a monospace character

I'm working on a code editor with syntax highlighting and I need to know the precise width of a monospace character. I use this value to calculate the position of a character on a line, which I need to know so I can place various GUI elements, such as text cursors (there can be multiple), selection rectangles, warning tooltips, etc. Up until now I've been using the following function:

    function getCharacterWidth(char, fontFamily, fontSize) {
        let span = document.createElement("span");
        span.style.fontFamily = fontFamily;
        span.style.fontSize = fontSize;
        span.style.position = "absolute";
        span.style.visibility = "hidden";
        span.style.width = "auto";
        span.style.whiteSpace = "nowrap";
        span.style.padding = "0";
        span.style.margin = "0";
        span.style.letterSpacing = "0px";
        span.style.wordSpacing = "0px";
        span.innerText = char;
        document.body.appendChild(span);
        
        let width = span.getBoundingClientRect().width;
        span.remove();
        
        return width;
    }

It has been working great, but then I noticed a problem on Google Chrome. When my text editor is rendering a large line, with thousands of characters, the character position is not being properly calculated because of rounding issues. It seems that on Google Chrome, the width returned by getBoundingClientRect() has a precision of at most 5 decimal places, which is not ideal for my use case. On Firefox, the precision seems to be much higher, going up to 15 decimal places, which is why I never had this problem there.

After some digging, I heard about this idea of calculating the width of a character based on the width of a span containing thousands of that character (https://stackoverflow.com/a/56379770/2197150). So, in my original function I replaced span.innerText = char with span.innerText = char.repeat(10000) and returned width / 10000. It helped, but the calculation is still perceptibly off when I'm dealing with large lines.

So here I am. How can I calculate the width of a character with high precision, like Firefox does, in other browsers?

about 4 years ago · Juan Pablo Isaza
2 Respuestas
Responde la pregunta

0

This is not a clean solution, but I suspect there is no really clean solution.

You could keep a "column map", based on the syntax formatting <span>s you already have flying around. Say your highlighter gives you:

<div class="line">
var long_line_of_variables = <span class="number">123</span>;
</div>

you could measure this span's left offset + the column it appears in (= length of textContent of the siblings before it = 29) and arrive at

colums = {
  '29': 290.456
}

Now you can interpolate that column 14 is at 290.456*14/29=140.22 pixels.
The more spans we add, the better we can guess:

colums = {
  '29': 290.456,
  '2900': 28997.000 // whoops, not what we would have calculated!
}

This method is heuristic, so you'll need to find a strategy, which will work across browsers, zoom/font-scale settings etc., including

  • adding more and more spans to this "map"
  • but maybe "cleaning" it once in a while?
  • keep a global map or one per line?
  • be smart about interpolation: pick the nearest map entry (in the best interval between entries)
  • add more "span probes"
    • split long lines without any formatting into chunks of N characters, wrapping them in spans, or inserting empty spans in between
    • maybe just a probe span right at the end of each line?

Having worked on similar problems and heuristics, my advice would be: don't. :)
It involves lots of tweaking and testing, and probably is a cross-platform nightmare (e.g. compare font rendering and rounding in Firefox on Win vs Linux vs MacOS vs iOS). Instead, try to attach anything to a localized span. I understand that this is probably way harder -- lots of text editors struggle with long lines, esp. when it comes to MB-size compiled JS...

about 4 years ago · Juan Pablo Isaza Denunciar

0

I came up with a solution that has been working well for my use case. The idea is to calculate the width of a character based on the largest line of my editor. My line elements look something like this:

<div class="line"><span class="keyword">let</span><span class="space"> </span><span class="identifier">foo</span></div>

If we want to calculate the width of a character based on that line, here is what we do:

let lineElement = document.querySelector(".line");
let lineWidth = lineElement.getBoundingClientRect().width;
let charWidth = lineWidth / lineElement.textContent.length;

I keep track of the largest line I've rendered in my editor. Whenever I render a new largest line, I update charWidth based on that new line.

This allows me to calculate the position of a character at any given column within a line by doing a simple multiplication column * charWidth. It has been working pretty well so far, even for lines with 100000+ characters.

There is, however, one last thing I had to do to handle even larger lines. It seems that there is a bug on Google Chrome where, when you try to render a line with a huge text node, e.g. <span>many many characters...</span>, it will not give you the correct width of the element (see Inaccurate width of large element on Chrome). To overcome this issue, whenever I have to render a huge text, I split it into multiple spans, e.g. <span>many many </span><span>characters...</span>.

Now I can render 500000 characters long lines with font size 48 with no problems. Above that, things start to get weird again, with miscalculations and other weird browser behaviors. So I decided to set a hard limit of 500000 characters rendered in a line. Everything beyond the 500000nth characters is hidden from the user.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda