在Javascript中将em转换为px(并获取默认字体大小)

时间:2012-05-05 15:58:22

标签: javascript css events dom

在某些情况下,获取em测量值的精确像素宽度非常有用。例如,假设您有一个具有CSS属性(如边框或填充)的元素,该元素以ems为单位测量,您需要获得边框或填充的精确像素宽度。有一个问题触及了这个主题:

How can i get default font size in pixels by using JavaScript or JQuery?

这个问题是关于获取默认字体大小 - 这是将相对em值转换为精确px值所必需的。

This answer对如何获取元素的默认字体大小有很好的解释:

  

由于ems测量宽度,您始终可以计算精确的像素字体   通过创建一个长度为1000 ems且除以它的div来确定大小   client-Width属性由1000.我似乎记得ems被截断为   最近的千分之一,所以你需要1000 ems以避免错误   截断像素结果。

因此,使用此答案作为指导,我编写了以下函数来获取默认字体大小:

function getDefaultFontSize(parentElement)
{
    parentElement = parentElement || document.body;
    var div = document.createElement('div');
    div.style.width = "1000em";
    parentElement.appendChild(div);
    var pixels = div.offsetWidth / 1000;
    parentElement.removeChild(div);
    return pixels;
}

获得默认字体大小后,只需将ems乘以元素的默认字体大小并将结果四舍五入,即可将任何em宽度转换为px宽度:

Math.round(ems * getDefaultFontSize(element.parentNode))

问题: getDefaultFontSize理想情况下应该是一个简单的,无副作用的函数,它返回默认的字体大小。但它 DOES 有一个不幸的副作用:它修改了DOM!它附加一个孩子,然后移除孩子。为了获得有效的offsetWidth财产,必须附加孩子。如果未将子div附加到DOM,则offsetWidth属性保持为0,因为该元素从未呈现。即使我们立即删除子元素,此函数仍然可以创建意外的副作用,例如触发正在侦听父元素的事件(如Internet Explorer的onpropertychange或W3C的DOMSubtreeModified事件)。

问题:有没有办法编写一个真正的副作用免费getDefaultFontSize()函数,它不会意外触发事件处理程序或导致其他意外的副作用?

5 个答案:

答案 0 :(得分:22)

编辑:不,没有。

获取给定元素的渲染字体大小,而不影响DOM:

parseFloat(getComputedStyle(parentElement).fontSize);

这是基于this question的答案。


修改

在IE中,您必须使用parentElement.currentStyle["fontSize"],但不能保证将大小转换为px。那就好了。

此外,此代码段不会获得该元素的默认字体大小,而是它的实际字体大小,如果它实际上有一个类和与之关联的样式,这很重要。换句话说,如果元素的字体大小为2em,则您将获得2个ems中的像素数。除非内联指定字体大小,否则您将无法获得正确的转换率。

答案 1 :(得分:7)

我有更好的答案。我的代码会将变量em中的1em值存储在unit-px中。

  1. 将此div放在HTML代码中的任意位置

    <div id="div" style="height:0;width:0;outline:none;border:none;padding:none;margin:none;"></div>
    
  2. 将此功能放在JavaScript文件中

    var em;
    function getValue(id){
        var div = document.getElementById(id);
        div.style.height = 1em;
        em = div.offsetHeight;
    }
    
  3. 现在,只要您在参数中调用此测试div的id为'getValue'的函数,您就会得到一个变量名em,它将在px中包含值1 em。

答案 2 :(得分:3)

如果你需要快速和脏的东西(并且基于身体的基本字体大小,而不是元素),我会选择:

Number(getComputedStyle(document.body,null).fontSize.replace(/[^\d]/g, ''))

 Number(  // Casts numeric strings to number
   getComputedStyle(  // takes element and returns CSSStyleDeclaration object
     document.body,null) // use document.body to get first "styled" element
         .fontSize  // get fontSize property
          .replace(/[^\d]/g, '')  // simple regex that will strip out non-numbers
 ) // returns number

答案 3 :(得分:1)

以下解决方案使用二进制搜索和window.matchMedia来查找窗口的em宽度。然后我们将窗口宽度与窗口像素宽度相除以得到最终结果。 没有DOM参与,副作用免费! 在性能方面,我的comp需要大约0.2ms的约2次迭代。

const singleEmInPixels = getSingleEmInPixels();
console.log(`1em = ${singleEmInPixels}px`);

/**
 * returns the amount of pixels for a single em
 */
function getSingleEmInPixels() {
    let low = 0;
    let high = 200;
    let emWidth = Math.round((high - low) / 2) + low;
    const time = performance.now();
    let iters = 0;
    const maxIters = 10;
    while (high - low > 1) {
        const match = window.matchMedia(`(min-width: ${emWidth}em)`).matches;
        iters += 1;
        if (match) {
            low = emWidth;
        } else {
            high = emWidth;
        }
        emWidth = Math.round((high - low) / 2) + low;
        if (iters > maxIters) {
            console.warn(`max iterations reached ${iters}`);
            break;
        }
    }
    const singleEmPx = Math.ceil(window.innerWidth / emWidth);
    console.log(`window em width = ${emWidth}, time elapsed =  ${(performance.now() - time)}ms`);
    return singleEmPx;
}

答案 4 :(得分:0)

我需要 em 尺寸,这就是我着陆的地方。我受到@10011101111 的解决方案的启发,编写了这个通用解决方案。请注意,这可能不一定没有 OP 要求的副作用,但可以对其进行调整以将副作用降至最低。

这个解决方案适用于那些和我有同样问题的人。

要获得全像素结果:

function getSize(size = '1em', parent = document.body) {
    let l = document.createElement('div')
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    size = l.clientHeight
    l.remove()
    return size
}

要获得更精确的结果:

function getSizePrecise(size = '1em', parent = document.body) {
    let l = document.createElement('div'), i = 1, s, t
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    t = l.clientHeight
    do {
        s = t
        i *= 10
        l.style.height = 'calc(' + i + '*' + size + ')'
        t = l.clientHeight
    } while(t !== s * 10)
    l.remove()
    return t / i
}

请注意,您可以传入任何大小的任何单位。例如:

getSizePrecise('1ex')
getSizePrecise('1.111ex')

您也可以通过删除 calc() 函数并直接将除单位之外的大小值与 JavaScript 中的乘数相乘来适应 CSS < 3 兼容性。

如果值溢出,这可能会返回 0。

在尝试计算溢出限制时,我在 Firefox 88.0 上达到了 17895696 或 17895697 的像素值。所以,我更新了上面的函数如下:

function getSizePrecise(size = '1em', parent = document.body) {
    let l = document.createElement('div'), i = 1, s, t
    l.style.visibility = 'hidden'
    l.style.boxSize = 'content-box'
    l.style.position = 'absolute'
    l.style.maxHeight = 'none'
    l.style.height = size
    parent.appendChild(l)
    t = l.clientHeight
    do {
        if (t > 1789569.6)
            break
        s = t
        i *= 10
        l.style.height = 'calc(' + i + '*' + size + ')'
        t = l.clientHeight
    } while(t !== s * 10)
    l.remove()
    return t / i
}

绝对不需要 .6(因为 t 可能是一个完整的值),但我将其留在那里以供参考。