为什么某些字体的元素的scrollHeight和clientHeight不相同?

时间:2018-10-15 11:28:21

标签: javascript html css

对于某些字体,当元素的line-height小于阈值时,scrollHeight大于clientHeight

因此,字体属性中有某些原因会导致这种情况,但这又是什么?如何最好避免使用CSS甚至自定义字体的字体编辑器来避免这种情况?

例如,在此摘录中,scrollHeight的{​​{1}}大于Tahoma,虽然clientHeightsans-serif为1时似乎还可以。当页面在Chrome(ctrl +)中缩放时,这种差异会增加,甚至在无衬线情况下也会发生。当行高低于1或字体大小变大时,差异会增加。

对于某些其他字体,它在行高1处上升到5px ,并通过将行高增加到2而减小,这导致计算不正确。

line-height
var a = document.getElementById('a');
console.log('tahoma - a.clientHeight: ' + a.clientHeight);
console.log('tahoma - a.scrollHeight: ' + a.scrollHeight);

var b = document.getElementById('b');
console.log('sans - b.clientHeight: ' + b.clientHeight);
console.log('sans - b.scrollHeight: ' + b.scrollHeight);

var c = document.getElementById('c');
console.log('sans - lineHeight:0.5 - c.clientHeight: ' + c.clientHeight);
console.log('sans - lineHeight:0.5 - c.scrollHeight: ' + c.scrollHeight);

var d = document.getElementById('d');
console.log('sans - font-size:200px - d.clientHeight: ' + d.clientHeight);
console.log('sans - font-size:200px - d.scrollHeight: ' + d.scrollHeight);
div[id] {
 overflow:auto;
 max-width:80%;
}

很明显,这种差异是由于溢出问题引起的,但是这里涉及哪些字体指标,以及我们如何识别<div id='a' style='font-family:tahoma; line-height:1;'>Hello</div> <div id='b' style='font-family:sans-serif; line-height:1;'>Hello</div> <div id='c' style='font-family:sans-serif; line-height:0.5;'>Hello</div> <div id='d' style='font-family:sans-serif; line-height: 1; font-size:500px;'>Hello</div>scrollHeight之间的差异?

在Chrome和Firefox中都发生;我没有测试其他浏览器。

2 个答案:

答案 0 :(得分:1)

我发现scrollHeight是元素内容的高度的量度,其中包括由于溢出而在屏幕上不可见的内容,而clientHeight是度量值元素高度的值。

当减小line-height时,div元素的高度变小-因此clientHeight会变小,但是内容的高度不会改变,因此scrollHeight会保持不变,因此这就是您进行2次测量的原因不同。

如果要两次不同的测量得出相同的结果,则必须修改容器元素的高度。例如,添加到div min-height: 1.2em

var a = document.getElementById('a');
console.log('tahoma - a.clientHeight: ' + a.clientHeight);
console.log('tahoma - a.scrollHeight: ' + a.scrollHeight);

var c = document.getElementById('c');
console.log('sans - lineHeight:0.5 - c.clientHeight: ' + c.clientHeight);
console.log('sans - lineHeight:0.5 - c.scrollHeight: ' + c.scrollHeight);
.div {
  font-size: 40px;
  margin: 10px;
  border: 1px solid black;
  min-height: 1.2em;
}
<div class='div' id='a' style='font-family:tahoma; line-height:1;'>Hello</div>
<div class='div' id='c' style='font-family:sans-serif; line-height:0.5;'>Hello</div>

显然,这会更改布局。在不更改布局的情况下,您将无法测量两种不同的东西,并且期望获得相同的结果。

如果要计算实际字符大小-可以使用this solution

console.log("tahoma size: " + measureTextHeight("40px tahoma"));
console.log("sans-serif size: " + measureTextHeight("40px sans-serif"));
console.log("Bookman Old Style size: " + measureTextHeight("40px Bookman Old Style"));
console.log("Palatino Linotype size: " + measureTextHeight("40px Palatino Linotype"));


function measureTextHeight(fontSizeFace) {

    // create a temp canvas
    var width=1000;
    var height=60;
    var canvas=document.createElement("canvas");
    canvas.width=width;
    canvas.height=height;
    var ctx=canvas.getContext("2d");

    // Draw the entire a-z/A-Z alphabet in the canvas
    var text="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    ctx.save();
    ctx.font=fontSizeFace;
    ctx.clearRect(0,0,width,height);
    ctx.fillText(text, 0, 40);
    ctx.restore();

    // Get the pixel data from the canvas
    var data = ctx.getImageData(0,0,width,height).data,
        first = false, 
        last = false,
        r = height,
        c = 0;

    // Find the last line with a non-transparent pixel
    while(!last && r) {
        r--;
        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                last = r;
                break;
            }
        }
    }

    // Find the first line with a non-transparent pixel
    while(r) {
        r--;
        for(c = 0; c < width; c++) {
            if(data[r * width * 4 + c * 4 + 3]) {
                first = r;
                break;
            }
        }

        // If we've got it then return the height
        if(first != r) return last - first;
    }

    // error condition if we get here
    return 0;
}
div{
  font-size: 40px;
}
<div style="font-family:tahoma;">Hello</div>
<div style="font-family:sans-serif;">Hello</div>
<div style="font-family:Bookman Old Style;">Hello</div>
<div style="font-family:Palatino Linotype;">Hello</div>

答案 1 :(得分:1)

为使问题更明显,我们在span内引入div并添加一些边框/背景。让我们开始使用一个大的line-height

body {
 font-family:sans-serif;
}
div {
  border:1px solid;
  margin:10px;
}
span {
  background:red;
}
<div style='line-height:3;'><span>Hello</span></div>

红色部分定义内容区域,边框所包围的空间是线框,它是我们div元素的高度(请查看此更多信息:Why is there space between line boxes, not due to half leading?)。

在这种情况下,我们没有任何溢出,因此scrollHeightclientHeight的值相同:

var a = document.getElementById('a');
console.log('clientHeight: ' + a.clientHeight);
console.log('scrollHeight: ' + a.scrollHeight);
body {
 font-family:sans-serif;
}
div {
  border:1px solid;
  margin:10px;
}
span {
  background:red;
}
<div id="a" style='line-height:3;'><span>Hello</span></div>

我们还可以得出结论,两者都等于3 * 16px,即line-height * font-size ref (默认情况下,font-size是{{1 }}。

现在,如果我们开始降低16px的音量,我们将在逻辑上减小div的高度,并且内容区域将保持不变:

line-height
body {
 font-family:sans-serif;
}
div {
  border:1px solid;
  margin:10px;
}
span {
  background:red;
}

现在很明显,我们已经溢出了,<div style='line-height:3;'><span>Hello</span></div> <div style='line-height:1;'><span>Hello</span></div> <div style='line-height:0.5;'><span>Hello</span></div> <div style='line-height:0.2;'><span>Hello</span></div>现在小于clientHeight,但是scrollHeight将保持clientHeight,而line-height * font-size将保留是红色部分的高度:

scrollHeight
var a = document.querySelectorAll('.show');
var b = document.querySelectorAll('.show span');

for(var i=0;i<a.length;i++) {
console.log('cH: ' + a[i].clientHeight + ' sH: ' + a[i].scrollHeight);
}
body {
 font-family:sans-serif;
 font-size:100px;
 padding-bottom:100px;
}
div.show {
  border:1px solid;
  margin:100px;
}
span {
  background:red;
}

但是为什么在内容区域保持不变的情况下<div class="show" style='line-height:3;'><span>Hello</span></div> <div class="show" style='line-height:1;'><span>Hello</span></div> <div class="show" style='line-height:0.5;'><span>Hello</span></div> <div class="show" style='line-height:0.2;'><span>Hello</span></div> <div class="show" style='line-height:0.1;'><span>Hello</span></div> <div class="show" style='line-height:0;'><span>Hello</span></div>的值却减小了?这是由于以下事实:我们在顶部和底部都有溢出(因为对齐方式是基线),并且scrollHeight仅包括底部溢出,因为顶部无法访问。要使scrollHeight等于内容区域,我们只需要更改对齐方式:

scrollHeight
var a = document.querySelectorAll('.show');
var b = document.querySelectorAll('.show span');

for(var i=0;i<a.length;i++) {
console.log('cH: ' + a[i].clientHeight + ' sH: ' + a[i].scrollHeight);
}
body {
 font-family:sans-serif;
 font-size:100px;
 padding-bottom:100px;
}
div.show {
  border:1px solid;
  margin:100px;
}
span {
  background:red;
  vertical-align:text-bottom;
}

现在很明显,如果<div class="show" style='line-height:3;'><span>Hello</span></div> <div class="show" style='line-height:1;'><span>Hello</span></div> <div class="show" style='line-height:0.5;'><span>Hello</span></div> <div class="show" style='line-height:0.2;'><span>Hello</span></div> <div class="show" style='line-height:0.1;'><span>Hello</span></div> <div class="show" style='line-height:0;'><span>Hello</span></div>足够大,并且line-height减小,line-height的最小值等于内容区域。

如果我们选中the specification,我们可以阅读以下内容:

  

“ height”属性不适用。 内容区域的高度   应基于字体但此规范未指定   如何。 UA可能会使用em-box或最大上升器,   字体的降序形式。 (后者将确保带有部分字形   在Em-box上方或下方仍位于内容区域内,但   导致用于不同字体的大小不同的框;前者会   确保作者可以控制相对于   “行高”,但导致字形在其内容之外绘画   区域。

     

注意:CSS的第3级可能会包含一个属性来选择   字体的度量用于内容的高度。

因此我们无法知道用于定义此区域的确切度量,这就是为什么每种字体的行为都不同的原因。我们只能知道它取决于scrollHeightfont-family。我们可能会通过一些测试手动找到计算结果。对于上面的示例,内容区域的高度似乎为font-size

对于1.12 * font-size,似乎是tahoma(在Chrome上)和1.206 * font-size(对于Firefox)(见下文):

1.21 * font-size
var a = document.querySelectorAll('.show');
var b = document.querySelectorAll('.show span');

for(var i=0;i<a.length;i++) {
console.log('cH: ' + a[i].clientHeight + ' sH: ' + a[i].scrollHeight);
}
body {
 font-family:tahoma;
 font-size:1000px;
 padding-bottom:100px;
}
div.show {
  border:1px solid;
  margin:100px;
}
span {
  background:red;
  vertical-align:text-bottom;
}

因此<div class="show" style='line-height:3;'><span>Hello</span></div> <div class="show" style='line-height:1;'><span>Hello</span></div> <div class="show" style='line-height:0.5;'><span>Hello</span></div> <div class="show" style='line-height:0.2;'><span>Hello</span></div> <div class="show" style='line-height:0.1;'><span>Hello</span></div> <div class="show" style='line-height:0;'><span>Hello</span></div>等于scrollHeight,其中p * font-size取决于字体,我们可以通过手动测试找到它,而p等于clientHeight 。当然,如果我们保持对齐基线,line-height * font-size会因为顶部溢出而有所不同。