61

How do I get the actual font face and font size of an element when the CSS font-face and font-size properties are not defined?

For example, the JavaScript snippet

object.style.fontFamily

does not return any value. That's pretty obvious, assuming CSS hasn't applied a style to object anywhere. But, of course, a certain font is used to render the text, probably the system font or the webbrowser default font.

So can, for instance, JavaScript get that rendered font?

MC Emperor
  • 22,334
  • 15
  • 80
  • 130
  • Java or Flash can do it. JS - cannot give you names of the font, but you can **try** to detect font by deciphering text render in canvas. – c69 Sep 16 '11 at 12:13
  • 1
    Don't know if it's any use to you, but I normally use the [WhatFont bookmarklet](http://chengyinliu.com/whatfont.html) to figure out what font's being used on a page, so there might be some ideas in that that could help you, if it successfully detects the fonts you're talking about... – Matt Gibson Sep 16 '11 at 12:17
  • 3
    Why do you want to do that? ..I shouldn't second-guess the questions of an Emperor but, I've always been a rebel anyways. – Joonas Sep 16 '11 at 12:18
  • @Lollero Lol! ;-) I want to determine the number of cols for a textarea based on the font defined for that textarea, since `cols` is a required attribute for textarea in XHTML. – MC Emperor Sep 22 '11 at 12:27
  • 1
    People who only need this information for debugging reasons, rather than actually needing to get it programatically with JavaScript, should check out http://stackoverflow.com/questions/884177/how-can-i-determine-what-font-a-browser-is-actually-using-to-render-some-text instead. Some browsers' dev tools offer this information. – Mark Amery Dec 20 '14 at 17:00

6 Answers6

55

I suggest this function:

function css( element, property ) {
    return window.getComputedStyle( element, null ).getPropertyValue( property );
}

Usage:

css( object, 'font-size' ) // returns '16px' for instance

Note: getComputedStyle doesn't work in IE8.

Live demo: http://jsfiddle.net/4mxzE/

console.log(
  getComputedStyle(document.getElementById('test'), null)
    .getPropertyValue('font')
)
#test {
  font-family: fantasy, cursive;
}
<div id="test">Lorem ipsum dolor sit font-face</div>
P Varga
  • 19,174
  • 12
  • 70
  • 108
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • 1
    Thanks, it seems it works. Small slice of code as well :-). I do not mind the fact that it's not working in IE8, because IE often lacks W3C standards and therefore it takes much time to make the layout of your website compatible with IE. – MC Emperor Sep 22 '11 at 12:18
  • 2
    IE has the comparable `element.currentStyle` instead: http://www.quirksmode.org/dom/getstyles.html – Stu Cox Jan 22 '13 at 13:46
  • If using jQuery, change `element` to `$(selector).get(index)`. The `.get()` method returns the DOM element(s) selected by `$(selector)`. Be sure to set `index = 0` even if there's only one element selected, since `.get()` without any params will return an array. – WNRosenberg Oct 08 '13 at 13:40
  • @Jazzerus I use `[0]` to access the first element of a jquery object. No need for a method when you can access it directly by array index. – Šime Vidas Oct 08 '13 at 14:11
  • 53
    Although this will still return the font the css specifies, not the font the text is actually rendered in, if they vary. I was looking for a way to see if the browser had downloaded and displayed my custom font. – cjspurgeon Apr 10 '14 at 21:57
  • In what case would that be different than jQuery's? Because both return '' (string with quotes) in some circumstances. – NoBugs Mar 06 '16 at 08:02
  • The jsfiddle provided doesn't seem to work as expected. I am on the Chrome browser. Here is my saved jsfiddle - http://jsfiddle.net/4mxzE/115/. The font I have specified is PT-Sans-Reg which it shows me in the alert, but the rendered font is still Times New Roman – Moiz Tankiwala Dec 02 '16 at 05:29
  • 2
    @MoizTankiwala I think, that's because `getComputedStyle` returns "computed values", instead of "used values" ([see examples here](https://drafts.csswg.org/css-cascade-4/#stages-examples)). – Šime Vidas Dec 02 '16 at 06:07
  • What's the purpose of `null` and `getPropertyValue`. Something line `getComputedStyle(document.body)["font-size"];` also seems to work but your approach may be better. – d.b Apr 02 '19 at 15:35
  • @d.b Yes, it should work. I’m not sure if the second argument is required in some very old browsers, but it’s probably not an issue anymore. – Šime Vidas Apr 03 '19 at 14:41
  • In chrome, `css(object, 'font-family' )` can check the font family used by a element. Or `css($0, 'font-family' )` if you just select the element. – Iceberg May 28 '20 at 15:48
  • is there a solution to the problem highlighted by @cjspurgeon ? 9 years has passed.... – aetonsi Aug 08 '23 at 09:02
45

There is no standard, reliable method for determining the actual font being used. The previous answers here will report the styled fontFamily style value, but that can be a list of font names and doesn't specifically identify the actual font rendered (which was the actual question posed here).

(As mentioned in some comments, there are ways to guess at the font by inspecting visual cues, but that isn’t likely to be 100% reliable.)

danorton
  • 11,804
  • 7
  • 44
  • 52
  • I found a way checking pixel by pixel, which you may say is a "visual cue" but what is a font if not a collection of pixels?... and if talks like a duck and swims like a duck... – Ivan Castellanos Aug 12 '16 at 05:28
  • 1
    Well, most contemporary fonts aren't collections of pixels, they're vector graphics. And this is not pointless nitpicking! What I'm getting at is that there will be differences in rendering between browsers - for example, kerning and ligatures can be supported in different ways (or not at all), and that will influence the dimensions of your rendered text. – ksadowski Jul 19 '17 at 17:33
  • I see your answer below now, and my point above doesn't really apply to it. Interesting approach :) – ksadowski Jul 19 '17 at 17:39
  • Chrome will now tell you which fonts are being rendered (as in Salman A's answer) - in Developer Tools, go to the Computed tab, and at the bottom there's a section for Rendered Fonts. – AJ Richardson Apr 25 '19 at 20:34
41

You can find the information about the rendered font in Chrome/Firefox Developer Tools. Try inspecting the paragraph in the following code snippet:

p { font-family: sans-serif;  }
<p>Some text and <span title="an emoji"></span></p>

In Chrome Developer Tools (tested on 55.0.2883.75 m 64-bit) you get the following information:

Chrome Developer Tools > Element > Computed Tab


In Firefox Developer Tools (tested on 47.0.2 with about:config > devtools.fontinspector.enabled = true) you get the following information:

Firefox Developer Tools > Element > Fonts Tab

Salman A
  • 262,204
  • 82
  • 430
  • 521
  • 1
    That doesn't show WHICH of those fonts was rendered =( If `Consolas` wasn't found, `Menlo` will be used. How to see that, even with Devtools? – Rudie Jul 12 '15 at 10:39
  • @Rudie: the panel (rendered fonts in Chrome, Fonts tab in FF) shows exactly which font was used. It is possible for a browser to use a font that is not listed in the font family. In the chrome example you can clearly see that "Lucida Sans Unicode" is not listed in font family. – Salman A Jul 12 '15 at 18:43
  • Well, damn, I'm obviously blind. The _Rendered fonts_ part is brilliant! And new-ish, I think, I've never seen it. Do you know if there's a way to get that info with JS? I'm searching the devtools code. Thanks man! =) – Rudie Jul 12 '15 at 20:15
  • It even detects the font if overridden with a custom `@font-face` with `src: local(Arial)`!! Sweeet. Exactly what I needed. – Rudie Jul 12 '15 at 20:18
  • 2
    Damn. Search ends at `this._agent.getPlatformFontsForNode()` which is not exposed. Calls https://github.com/ChromiumWebApps/blink/blob/ea798590bf47f65436cd9d0803b3d7af4d5d614f/Source/core/inspector/InspectorCSSAgent.cpp#L772 later, in case you're interested. – Rudie Jul 12 '15 at 20:57
  • 1
    @Rudie If you still need it, I've made this fiddle http://jsfiddle.net/JulienCabanes/ubzrmmy5/ – Julien Cabanès Sep 30 '15 at 01:59
  • @JulienCabanès That's font guessing. That's not acurate for missing font names and different parents. Set ``'s font-family to "Foo" and the type in "Bar". It renders Arial (my default), but displays "Foo". Still useful though. Have it on Github or something somewhere? – Rudie Sep 30 '15 at 17:04
  • @Rudie Here is the [Github repo](https://github.com/JulienCabanes/Font-Guess). The issues you're mentioning are solved in this version. – Julien Cabanès Oct 01 '15 at 02:18
  • Firebug on Firefox does something similar, where in the CSS definition for a single element, the rendered font is active and the others are greyed out – Izkata Apr 18 '16 at 14:10
  • @Rudie You probably don't need it anymore but my answer shows how to do this without "guessing" because it checks each pixel instead. – Ivan Castellanos Aug 12 '16 at 05:25
  • I am using Chrome Version 54.0.2840.99 m but don't see the actual rendered fonts as shown in the image of this response. What version is that? – Moiz Tankiwala Dec 02 '16 at 05:23
  • @MoizTankiwala tested again on latest version of Chrome. – Salman A Dec 02 '16 at 08:41
  • I see them now. Don't know if it had got to do with my browser version. Thanks @Salman – Moiz Tankiwala Dec 20 '16 at 14:23
13

I found a way (for all browsers which support <canvas>), by checking pixel for pixel if the font rendering has changed

function renderedfont(ele) {
    var getDefaultFonts = function () {
        var iframe = document.createElement('iframe');
        var html = '<html><body>';
        var fonts;
        document.body.appendChild(iframe);
        iframe.contentWindow.document.open();
        iframe.contentWindow.document.write(html);
        var subele = iframe.contentWindow.document.createElement(ele.tagName);
        iframe.contentWindow.document.body.appendChild(subele);
        fonts = getComputedStyle(subele)['font-family'];
        document.body.removeChild(iframe);
        return fonts;
    }
    var fonts = getComputedStyle(ele)['font-family'] + ',' + getDefaultFonts();
    var fontsArray = fonts.split(',');
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext("2d");
    var testString = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()ñ";
    var prevImageData;
    document.body.appendChild(canvas);
    canvas.width = 500;
    canvas.height = 300;
    fontsArray.unshift('"Font That Doesnt Exists ' + Math.random() + '"');

    for (var i = 0; i < fontsArray.length; i++) {
        var fontName = fontsArray[i].trim();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.font = '16px ' + fontName + ', monospace';
        ctx.fillText(testString, 10, 100);
        var idata = ctx.getImageData(0, 0, canvas.width, canvas.height); 
        var data = idata.data
        if (prevImageData) {
            for (var j = 0; j < data.length; j += 3) {
                if (prevImageData[j + 3] !== data[j + 3]) {
                    document.body.removeChild(canvas);
                    return fontName;
                }
            }
        }
        prevImageData = data;
    }

    document.body.removeChild(canvas);
    return 'monospace';
}

So to use you just do:

renderedfont(document.body);
// Arial

If you are using this for another dialect (e.g. japanese) you may want to change the testString variable to the most common characters in that dialect.

Ivan Castellanos
  • 8,041
  • 1
  • 47
  • 42
  • That's a cool method, but it still doesn't work, because it wouldn't know which fonts to try. You can name a font Foo, but still render Arial, or reference monospace, which renders Consolas. Chrome knows, but doesn't expose that info =( The devtools have a _Rendered fonts_ section in _Computed_ which is always correct, but it uses those hidden methods. – Rudie Aug 12 '16 at 17:53
  • 1
    First part: This methods discards fake fonts, so if you use "Foo" it returns the actual one being rendered, not "Foo" because this methods checks agains default fonts by creating an isolated copy of the node . Seconds part: True, it returns "monospace" when is rendering "Consolas" but to fix it would be easy enough, you just have to try to render "Consolas" and check if its rendering the same thing as "monospace". The convertions are just a handful (Fantasy, monospace, serif) so its possible. And you could send the image to the server to check there against all fonts. – Ivan Castellanos Aug 13 '16 at 01:48
2

Being a bit late here, but having had to solve the same problem... Šime Vidas's answer is basically correct, but nowadays, you can get creative, and have your answer.

Basically, define your own font-face, which you are sure does not match any existing font. Then, add your font after each computedstyle font, and see if it's your font that get rendered. If it's not, congrats, you found the rendered font

Here is the fiddle: https://jsfiddle.net/obutjanw/

this is the code that defines the function you need:

    (function(win) {

        var style = null;
        function createClass(name,rules){
            if (typeof(rules)!="string") {
                rules = JSON.stringify(rules).trim();
            }

            if (rules.startsWith("{")&&rules.endsWith("}")) {
                rules = rules.substring(1,rules.length-1);
            }

            if (style==null) {
                style = document.createElement('style');
                style.type = 'text/css';
                var head = document.getElementsByTagName('head');
                if (head.length=0) {
                    var h = document.createElement('head');
                    document.insertBefore(h,document.body);
                    head = [h];
                }
                head[0].appendChild(style);
            }


            var rule;
            if(!(style.sheet||{}).insertRule) {
                rule = (style.styleSheet || style.sheet).addRule(name, rules);
            } else {
                style.sheet.insertRule(name+"{"+rules+"}",0);
                rule = style.sheet
            }
            return rule;
        }

        function removeNode(node) {
            if (node.remove) {
                node.remove();
            } else {
                var pn = node.parentElement;
                if (pn!=null) {
                    pn.removeChild(node);
                }
            }
        }



        createClass("@font-face", "{ font-family: 'void';  src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPQAA0AAAAAJLwAAAN2AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCehEICrcUtUcLgVYAATYCJAODGgQgBYQrB4IOGy8iyD4M2GTIPrT6WF2CYVmsrZSME0Lxo0HkLsnMe3J+bjz8/1jtvj/u6wnzjEfTZlY97lD30CyaNpNQaV4SibwX23xgzQx8YoKmJQvZq6ZBWjJtbams7UgryfyPub1/XCfUSbdKmihiPrNKphKi5QzxL7t7f4Ygsik2JcsCqWjBhCpFOWuA+r3/lb/f/ynl4AsvOYHFxycux78I1qIX+gd5kXU+ZsO7wLswbYM/aGDRxjV8TPCAshIIMC3BsAwDkAAb0NvIC+Bt1UII+PmLFgF/hd0tbtmBKvpAEsJFJIuEZDoXOdKdvlnrANSzuI5ODoqTBnPXWUMoMLdg3mfM5HgyMvzfCugCzEGQYQ45BQFX1ASmGBwDbTaYhJrjHt5r10f62D9QM6cHAk2AuaL8WWyod/QWEE4QLaFWFCqRyTS5fKGWdOtLt+d0B+sPBEPhSDQWTyRT6Uw2ly8US+VKtVZv8IIoyYqqQd9Ny3bcZqvd6fb6g+FoPJnO5ovlar3Z7vYA5ScZ7ex193Bz+4z/ClwC/AB8oXoA1Q/QBEggoUqTYA8V8QP3xWofHHUvrXZRqXfN8qbGV8YZV/BYWfkBegAgFnoLMY558+Pt8rg7Lt/z7D56vHHjeA/kHpUZZIyeL9n5vJD5V237BwsBgH8gRAhBCAmRRAYRurBwyYHg13/L7+pCvTFrmXM6isP3aQYoSiBYmswqCfwfekQgMH26vOBlp5nIt6nNIJs88OmzLcScJXAJBwUBOeW2FwjQk4KAkLULgaTgJAIZWZcRyMq6KUBOxX0B8rLeC1DT8waBuokUYKIpYQXQyFC0nUzq9pMZRReBLHKDzOlbk3lFr8maFR5HqdvkgxFXG6MrdRFjNCViJUaL5+QiVrjAwbN9F7h5QMLZB9QKuCbhOcOWi97AARc3ldBBDi8bFfJTmJ/1iItcwZOIMfIycmDvY6ScyfmvdqFTF5QimmrIbmrhJOdJlQqCBU9IMJG7EXVZ2rAZzfk/h3ThhdtKRD2rDSRULcKD5KXtU+4+qq9BUUlZRVVNXUOTnYOTi5sHjkCi0BgsjpePX0BQSFhEVExcQlJKWkZWTl5BUUlZRVVNXQNPIJLIFCqNwWSxOVxNLW0dXT19A0MjYxNTM3MLSytrG1s7ewffpMhE1htvffTO++5q7O2mciQwb+6tAgAAAA==) format('woff2'), url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); }");


        var tests = document.createElement("span");
        tests.innerHTML = "0123";
        tests.style.display = "inline-block";
        tests.style.fontFamily = "void";
        document.body.appendChild(tests);
        setTimeout(function() {
            removeNode(tests);
        },0);

        function getRenderedFontFamily(elm, computedstyle) {
            var cs = (typeof(computedstyle) == 'undefined') ? win.getComputedStyle(elm) : computedstyle;
            var fontF   = (cs.fontFamily        || elm.style['font-family'] || '').replace(/['"]*/g, '');
            var tfontF = fontF.split(",");
            var tests = document.createElement("span");
            tests.innerHTML = "0123";
            tests.style.display = "inline-block";
            tests.style.fontFamily = "void";
            elm.appendChild(tests);
            var refcs = window.getComputedStyle(tests);
            var refw = refcs.width;
            var tested = {};
            while (tfontF!=null) {
                for (var i=0;i<tfontF.length;i++) {
                    if (tested[tfontF[i]]) continue;
                    tests.style.fontFamily = tfontF[i] + ", void";
                    if (refcs.width!=refw) {
                        removeNode(tests);
                        return tfontF[i].trim();
                    }
                    tested[tfontF[i]] = true;
                }
                if (elm.parentElement) {
                    elm = elm.parentElement;
                    var cs1 = win.getComputedStyle(elm);
                    fontF   = (cs1.fontFamily       || elm.style['font-family'] || '').replace(/['"]*/g, '');
                    tfontF = fontF.split(",");
                } else {
                    tfontF = null;
                }
            }
            removeNode(tests);
        }
        win.getRenderedFontFamily =  getRenderedFontFamily;
    })(window);

Note that you really should keep the part were a node using the font is appended on load, as otherwise the browser may be late rendering the corect font on the first call to getRenderedFontFamily

0

There is a way to determine the loaded font using the FontFaceSet check method.

getComputedStyle(element).fontFamily
  .split(', ')
  .find(font => document.fonts.check(`10px ${font}`));

You may also want to add font style/weight to the check if that matters in your case.

But do note that if the font family set contains an invalid font entry it may return an incorrect result (see note in documentation https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/check#nonexistent_fonts).

P. Galbraith
  • 2,477
  • 21
  • 24