69

在某些情况下,获得em测量的准确像素宽度可能很有用。例如,假设您有一个元素具有以 em 为单位测量的 CSS 属性(如边框或填充),并且您需要获取边框或填充的确切像素宽度。有一个涉及此主题的现有问题:

如何使用 JavaScript 或 JQuery 获取以像素为单位的默认字体大小?

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

这个答案很好地解释了如何获取元素的默认字体大小:

由于 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;
}

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

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

问题:理想情况下,getDefaultFontSize它应该是一个简单、无副作用的函数,它返回默认字体大小。但它确实有一个不幸的副作用:它修改了 DOM!它附加一个孩子,然后删除孩子。为了获得有效的offsetWidth属性,附加孩子是必要的。如果您不将子元素附加div到 DOM,则该offsetWidth属性将保持为 0,因为该元素永远不会被渲染。即使我们立即删除子元素,此函数仍会产生意想不到的副作用,例如触发正在侦听父元素的事件(如 Internet Exploreronpropertychange或 W3C 的DOMSubtreeModified事件)。

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

4

6 回答 6

46

编辑:不,没有。

在不影响 DOM 的情况下获取给定元素的渲染字体大小:

parseFloat(getComputedStyle(parentElement).fontSize);

这是基于这个问题的答案。


编辑:

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

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

于 2012-05-05T21:05:06.380 回答
10

我有更好的答案。1em我的代码将在 JavaScript 变量中存储(以 CSS 像素px为单位)的长度em

  1. 将它div放在 HTML 代码中的任何位置

    <div id="div" style="height:0;width:0;outline:none;border:none;padding:none;margin:none;box-sizing:content-box;"></div>
    
  2. 将此函数放在您的 JavaScript 文件中

    var em;
    function getValue(id){
        var div = document.getElementById(id);
        div.style.height = '1em';
        return ( em = div.offsetHeight );
    }
    

现在,每当您'getValue'在参数中使用该测试 div 的 id 调用此函数时,您将拥有一个变量名称em,其中包含 1 em 的值(以 px 为单位)。

于 2016-09-03T12:57:42.070 回答
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
于 2012-05-05T21:40:08.013 回答
1

以下解决方案使用二分搜索和 window.matchMedia 来找出窗口的 em 宽度。然后我们将窗口 em 宽度除以窗口像素宽度得到最终结果。不涉及 DOM,无副作用!在性能方面,我的 comp 大约需要 8 次迭代,大约 0.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;
}
于 2018-03-27T10:49:07.643 回答
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 可能是一个完整的值),但我将它留在那里以供参考。

于 2021-05-01T18:47:05.600 回答
0

以下 JS 函数将任何 CSS 长度单位值转换为另一个。
干杯!

// Usage examples: convertCSSLengthUnit('26.556016597510375mm'); convertCSSLengthUnit('26.556016597510375mm', 'px'); convertCSSLengthUnit('100px', 'mm');
function convertCSSLengthUnit(fromUnitValue, toUnit){
  let value = fromUnitValue.match(/[0-9]+\.*[0-9]*/g)[0];
  let unit = fromUnitValue.match(/[a-zA-Z]+/g)[0];

  let frag = document.createRange().createContextualFragment(`
    <div style='all: initial; pointer-events: none; display: block; position: absolute; border: none; padding: 0; margin: 0; background: rgba(0,0,0,0); color: color: rgba(0,0,0,0); width: 1${unit}; height: 1px;'></div>
  `);
  document.body.appendChild(frag);
  let measureElement = document.body.children[document.body.children.length-1];
  let toUnitValuePixel = measureElement.getBoundingClientRect().width * value; // X
  measureElement.remove();

  if(toUnit){
    let frag = document.createRange().createContextualFragment(`
      <div style='all: initial; pointer-events: none; display: block; position: absolute; border: none; padding: 0; margin: 0; background: rgba(0,0,0,0); color: color: rgba(0,0,0,0); width: 1${toUnit}; height: 1px;'></div>
    `);
    document.body.appendChild(frag);
    let measureElement = document.body.children[document.body.children.length-1];
    let valueUnit = measureElement.getBoundingClientRect().width; // Y
    measureElement.remove();

    // Given: Aem and Bmm with B=1. We know: Aem = Xpx and Bmm = Ypx. Therefore: 1px = Bmm/Y Result: Aem = Xpx = X * 1px = X * Bmm/Y <=> Aem = X * 1mm/Y (with B=1) <=> Aem = X/Ymm
    return (toUnitValuePixel / valueUnit) + toUnit;
  }
  return toUnitValuePixel + 'px';
}
于 2022-01-10T13:57:38.050 回答