admin管理员组文章数量:1293705
For some fonts when the line-height
of the element is smaller than a threshold the scrollHeight
is bigger than the clientHeight
.
So there is something in the font properties that causes this but what is this and how can it be avoided preferably using CSS or even a font editor for custom fonts?
For example, in this snippet the scrollHeight
for Tahoma
is more than the clientHeight
although sans-serif
seems OK when line-height
is 1. This difference increases when the page is zoomed in Chrome (ctrl+) and happens even for sans-serif. When the line-height is below 1 or the font-size gets bigger the difference increases.
For some other fonts it goes up to 5px at line-height 1 and decreases by increasing the line-height up to 2 which leads to incorrect calculation.
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>
For some fonts when the line-height
of the element is smaller than a threshold the scrollHeight
is bigger than the clientHeight
.
So there is something in the font properties that causes this but what is this and how can it be avoided preferably using CSS or even a font editor for custom fonts?
For example, in this snippet the scrollHeight
for Tahoma
is more than the clientHeight
although sans-serif
seems OK when line-height
is 1. This difference increases when the page is zoomed in Chrome (ctrl+) and happens even for sans-serif. When the line-height is below 1 or the font-size gets bigger the difference increases.
For some other fonts it goes up to 5px at line-height 1 and decreases by increasing the line-height up to 2 which leads to incorrect calculation.
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>
It's clear that this difference is due to an overflow issue but what font metrics are involved here and how can we identify the difference between scrollHeight
and clientHeight
?
This happens both in Chrome and Firefox; I didn't test other browsers.
Share Improve this question edited Oct 15, 2018 at 15:08 Boann 50.1k16 gold badges124 silver badges152 bronze badges asked Oct 15, 2018 at 11:28 AliAli 22.3k15 gold badges85 silver badges99 bronze badges 18-
add
overflow: auto;
and you will notice this visually .. this is font specific, each font has its own design and using line-height:1 for tahoma create an overflow, the space needed to draw the font is not enough. – Temani Afif Commented Oct 15, 2018 at 11:32 - 1 check this it may help you : stackoverflow./questions/27631736/… / stackoverflow./questions/42026239/… / stackoverflow./questions/25520410/… – Temani Afif Commented Oct 15, 2018 at 11:43
- 1 @LGSon opentype.js it can be used both in node and browser. – Ali Commented Oct 15, 2018 at 11:49
-
1
I have to ask, as interesting as it is, why do you care? The bottom line is that you know which fonts overflow by calculating
scrollHeight-clientHeight
, this should be enough to identify the problem and fix it with JS. CSS don't have access to the inner structure of the font, so you won't be able to do such calculation with CSS alone. If you want to know how to alter fonts, I guess stackoverflow is not the preferable platform. You may try : graphicdesign.stackexchange. – Itay Gal Commented Oct 15, 2018 at 17:28 - 1 What do you actually want to be able to do with this knowledge? As you wrote in another ment "It cause wrong calculation for expanding elements", I start thinking you have an X/Y problem here, so instead explain/show what you are trying to achieve and how this discrepancy breaks it. – Asons Commented Oct 15, 2018 at 18:32
2 Answers
Reset to default 3To make the issue more visible let's introduce a span
inside the div
and add some border/background. Let's start by using a big 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>
The red part define the content area and the space surrounded by the border is the line box which is the height of our div element (check this more information: Why is there space between line boxes, not due to half leading?).
In this case, we don't have any overflow so both scrollHeight
and clientHeight
will give the same value:
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>
We can also conclude that both are exacly equal to 3 * 16px
which is line-height * font-size
ref (by default the font-size
is 16px
).
Now if we start deacring the line-height
we will logically decrease the height of the div and the content area will remain the same:
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>
Now it's clear that we have overflow and the clientHeight
will now be less than the scrollHeight
but the clientHeight
will remain line-height * font-size
while the scrollHeight
will be the height of the red part:
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>
But why the value of scrollHeight
is decreasing while the content area is kept the same? This is due to the fact that we are having an overflow on the top and the bottom (because the alignment is baseline) and the scrollHeight
include only the bottom overflow as the top bee inaccessible. To make the scrollHeight
equal to the content area we simply need to change the alignment:
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>
Now it's clear that if the line-height
is big enough both are equal and if the line-height
is reduced the scrollHeight
has a min value equal to the content area.
If we check the specification we can read this:
The 'height' property does not apply. The height of the content area should be based on the font, but this specification does not specify how. A UA may, e.g., use the em-box or the maximum ascender and descender of the font. (The latter would ensure that glyphs with parts above or below the em-box still fall within the content area, but leads to differently sized boxes for different fonts; the former would ensure authors can control background styling relative to the 'line-height', but leads to glyphs painting outside their content area.)
Note: level 3 of CSS will probably include a property to select which measure of the font is used for the content height.
So we cannot know the exact metrics used to define this area, that's why it behave differently for each font. We can only know that it depends on the font-family
and the font-size
. We may probably find the calculation manually doing some tests. For the above example, the height of the content area seems to be 1.12 * font-size
For tahoma
it seems to be 1.206 * font-size
(on Chrome) and 1.21 * font-size
(on Firefox) (see below):
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>
So scrollHeight
is equal to p * font-size
where p
depends on the font and we can find it manually doing some tests and clientHeight
is equal to line-height * font-size
. Of course, if we keep the alignment baseline, scrollHeight
will be different because of the top overflow.
I found that scrollHeight
is a measurement of the height of an element's content, including content not visible on the screen due to overflow, while the clientHeight
is a measurement of the height of an element.
When you reduce the line-height your div element's height is getting smaller - so the clientHeight will be smaller but the content's height won't be changed, hence the scrollHeight will remain the same, so this is the reason your 2 measurements differs.
If you want 2 different measurements to give the same results, you'll have to modify the container element's height. For example add to the 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>
Obviously, this changes the layout. Without changing the layout you won't be able to measure 2 different things and expect to get the same result.
If you want to calculate the real char size - you can use 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>
版权声明:本文标题:javascript - Why are an element's scrollHeight and clientHeight not the same for some fonts? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741582863a2386672.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论