admin管理员组

文章数量:1429735

I've read about offsetHeight, clientHeight and scrollHeight. Still I can't figure out how to set the correct height to a div that can include:

  • box-sizing
  • padding
  • margin
  • border
  • something else that may push the div

In my example below I've made 4 sections. The first section is untouched so the height on that one is auto and correct. When I try to set the height in the other sections with offsetHeight, clientHeight and scrollHeight the results are no longer correct.

How can I calculate it in a way that it's always working? I've seen many answers here on Stackoverflow but no reliable solution.

window.addEventListener('DOMContentLoaded', (event) => {
  let items1 = document.querySelectorAll('.section1 div');
  let items2 = document.querySelectorAll('.section2 div');
  let items3 = document.querySelectorAll('.section3 div');
  
  items1.forEach((item) => {    
    item.style.height = item.offsetHeight + 'px';
  });
  
  items2.forEach((item) => {    
    item.style.height = item.clientHeight + 'px';
  });
  
  items3.forEach((item) => {    
    item.style.height = item.scrollHeight + 'px';
  });
});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
 }
 
 .div3 {
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div4 {
  padding: .5rem;
  margin: .5rem;  
 }
 
 .div5 {
 }
 
 div[class^=div] {
   background: #eee;
   outline: 1px solid red;
 }
 
 body {
   display: flex;
 }
 
 section {
   background: #f5f5f5;
   margin: .5rem;
   width: 100px;
 }
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section1">
  <div class="div1">Some<br>text</div>
  <div class="div2">Too<br>High</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section2">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section3">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

I've read about offsetHeight, clientHeight and scrollHeight. Still I can't figure out how to set the correct height to a div that can include:

  • box-sizing
  • padding
  • margin
  • border
  • something else that may push the div

In my example below I've made 4 sections. The first section is untouched so the height on that one is auto and correct. When I try to set the height in the other sections with offsetHeight, clientHeight and scrollHeight the results are no longer correct.

How can I calculate it in a way that it's always working? I've seen many answers here on Stackoverflow but no reliable solution.

window.addEventListener('DOMContentLoaded', (event) => {
  let items1 = document.querySelectorAll('.section1 div');
  let items2 = document.querySelectorAll('.section2 div');
  let items3 = document.querySelectorAll('.section3 div');
  
  items1.forEach((item) => {    
    item.style.height = item.offsetHeight + 'px';
  });
  
  items2.forEach((item) => {    
    item.style.height = item.clientHeight + 'px';
  });
  
  items3.forEach((item) => {    
    item.style.height = item.scrollHeight + 'px';
  });
});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
 }
 
 .div3 {
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div4 {
  padding: .5rem;
  margin: .5rem;  
 }
 
 .div5 {
 }
 
 div[class^=div] {
   background: #eee;
   outline: 1px solid red;
 }
 
 body {
   display: flex;
 }
 
 section {
   background: #f5f5f5;
   margin: .5rem;
   width: 100px;
 }
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section1">
  <div class="div1">Some<br>text</div>
  <div class="div2">Too<br>High</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section2">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section3">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

Share Improve this question asked Nov 13, 2019 at 8:57 Jens TörnellJens Törnell 24.9k46 gold badges130 silver badges223 bronze badges 2
  • What do you mean by "correct height"? You mean the natural height the div gets when its children are created in the DOM? – user5834627 Commented Nov 13, 2019 at 9:50
  • The inner height if you will. None of the last three sections matches the first one. I've added my own answer to this question which I think is correct. – Jens Törnell Commented Nov 13, 2019 at 10:24
Add a ment  | 

2 Answers 2

Reset to default 1

The issue is that you are randomly applying the box-sizing. You should apply them to all the elements inside the same section or not at all but not to only few of them.

The correct result is the first one with box-sizing:border-box applied.

window.addEventListener('DOMContentLoaded', (event) => {
  let items1 = document.querySelectorAll('.section1 div');
  let items2 = document.querySelectorAll('.section2 div');
  let items3 = document.querySelectorAll('.section3 div');

  items1.forEach((item) => {
    item.style.height = item.offsetHeight + 'px';
  });

  items2.forEach((item) => {
    item.style.height = item.clientHeight + 'px';
  });

  items3.forEach((item) => {
    item.style.height = item.scrollHeight + 'px';
  });
});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
}

.div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
}

.div3 {
  padding: .5rem;
  margin: .5rem;
}

.div4 {
  padding: .5rem;
  margin: .5rem;
}

div[class^=div] {
  background: #eee;
  outline: 1px solid red;
}

body {
  display: flex;
}

section {
  background: #f5f5f5;
  margin: .5rem;
  width: 100px;
}

.section1 >*{
  box-sizing: border-box;
}
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section1">
  <div class="div1">Some<br>text</div>
  <div class="div2">Too<br>High</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section2">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section3">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

Adding box-sizing to the other will make them smaller because both don't include the border in the calculation and the value given will later include the border.

window.addEventListener('DOMContentLoaded', (event) => {
  let items1 = document.querySelectorAll('.section1 div');
  let items2 = document.querySelectorAll('.section2 div');
  let items3 = document.querySelectorAll('.section3 div');

  items1.forEach((item) => {
    item.style.height = item.offsetHeight + 'px';
  });

  items2.forEach((item) => {
    item.style.height = item.clientHeight + 'px';
  });

  items3.forEach((item) => {
    item.style.height = item.scrollHeight + 'px';
  });
});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
}

.div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
}

.div3 {
  padding: .5rem;
  margin: .5rem;
}

.div4 {
  padding: .5rem;
  margin: .5rem;
}

div[class^=div] {
  background: #eee;
  outline: 1px solid red;
}

body {
  display: flex;
}

section {
  background: #f5f5f5;
  margin: .5rem;
  width: 100px;
}

.section1 >*,
.section2 >*,
.section3 >*{
  box-sizing: border-box;
}
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section1">
  <div class="div1">Some<br>text</div>
  <div class="div2">Too<br>High</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section2">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section3">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

Note that scrollHeight and clientHeight are the same here since there is no overflow.


Typically, offsetHeight is a measurement in pixels of the element's CSS height, including any borders, padding, and horizontal scrollbars (if rendered). ref

The scrollHeight value is equal to the minimum height the element would require in order to fit all the content in the viewport without using a vertical scrollbar. The height is measured in the same way as clientHeight: it includes the element's padding, but not its border, margin or horizontal scrollbar (if present). It can also include the height of pseudo-elements such as ::before or ::after. If the element's content can fit without a need for vertical scrollbar, its scrollHeight is equal to clientHeight ref

UPDATE

If you want a generic way then you need to test the box-sizing. If border-box you consider offsetHeight, if not you consider clientHeight minus padding:

window.addEventListener('DOMContentLoaded', (event) => {
  let items = document.querySelectorAll('section:not(.correct) div');
  
  items.forEach((item) => {
    var e = window.getComputedStyle(item);
    var b = e.boxSizing;
    if(b =="border-box")
      item.style.height = item.offsetHeight + 'px';
    else 
      var p = parseFloat(e.paddingTop) + parseFloat(e.paddingBottom);
      item.style.height = (item.clientHeight - p) + 'px';
  });

});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
 }
 
 .div3 {
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div4 {
  padding: .5rem;
  margin: .5rem;  
 }
 
 .div5 {
 }
 
 div[class^=div] {
   background: #eee;
   outline: 1px solid red;
 }
 
 body {
   display: flex;
 }
 
 section {
   background: #f5f5f5;
   margin: .5rem;
   width: 100px;
 }
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section1">
  <div class="div1">Some<br>text</div>
  <div class="div2">Too<br>High</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section2">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section3">
  <div class="div1">Too<br>low</div>
  <div class="div2">Too<br>high</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Too<br>high</div>
  <div class="div5">Some<br>text</div>
</section>

After many tests I've figured it out. The solution is to use getComputedStyle(item).getPropertyValue('height').

In the below example the first one is untouched and the second one set the height with the above.

window.addEventListener('DOMContentLoaded', (event) => {
  let items4 = document.querySelectorAll('.section4 div');
  
  items4.forEach((item) => {    
    item.style.height = getComputedStyle(item).getPropertyValue('height');
  });
});
.div1 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div2 {
  border: 5px solid #fff;
  padding: .5rem;
  margin: .5rem;
 }
 
 .div3 {
  padding: .5rem;
  margin: .5rem;
  box-sizing: border-box;
 }
 
 .div4 {
  padding: .5rem;
  margin: .5rem;  
 }
 
 .div5 {
 }
 
 div[class^=div] {
   background: #eee;
   outline: 1px solid red;
 }
 
 body {
   display: flex;
 }
 
 section {
   background: #f5f5f5;
   margin: .5rem;
   width: 100px;
 }
<section class="correct">
  <div class="div1">Some<br>text</div>
  <div class="div2">Some<br>text</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Some<br>text</div>
  <div class="div5">Some<br>text</div>
</section>

<section class="section4">
  <div class="div1">Some<br>text</div>
  <div class="div2">Not too<br>High =)</div>
  <div class="div3">Some<br>text</div>
  <div class="div4">Not too<br>high =)</div>
  <div class="div5">Some<br>text</div>
</section>

本文标签: