Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating element width in JavaScript gives wrong results

Tags:

javascript

css

There is a simple div in the document with some styles. Its width is not set in HTML nor in CSS. I tried to calculate its actual width in JavaScript using different methods:

  • offsetWidth
  • clientWidth
  • getBoundingClientRect().width
  • getComputedStyle(element).getPropertyValue('width')

but none of them returned the actual width which can be seen in Chrome Developer Tools.

Here is a code that demonstrate this issue:

var resumeHeader = document.querySelector('#resume-header');
var resumeHeaderComputedWidth = getComputedStyle(resumeHeader, null).getPropertyValue('width');
console.log(resumeHeaderComputedWidth)
console.log(resumeHeader.offsetWidth);
console.log(resumeHeader.clientWidth)
console.log(resumeHeader.getBoundingClientRect().width)
#resume-header {
        display: inline-block;
        background-color: #F5D061;
        padding: 2px 6px;
        font-weight: bold;
        font-size: 14px;
        position: relative;
        top: 25px;
        left: 0;
        transition: width 1s ease;
        font-family: "Merriweather", serif;
        line-height: 1.8;
        letter-spacing: 1px;
        box-sizing: border-box;
}
<link href="https://fonts.googleapis.com/css?family=Merriweather:300,400" rel="stylesheet">
<div id="resume-header">
    Resume
</div>

Please note that in the above snippet, the calculated width is sometimes correct and sometimes wrong, while in Chrome, it is always wrong.

like image 920
Ammar Alyousfi Avatar asked Oct 16 '17 14:10

Ammar Alyousfi


People also ask

How is div width determined?

div tags are block level elements. All block level elements inherit the width of their parent element by default. In your example, the div is inheriting the width of the parent body tag, taking into account any margin or padding on the body element.

Which method is useful to get the width and height of the rendered element?

getBoundingClientRect() , when there aren't any transforms applied to the element. In case of transforms, the offsetWidth and offsetHeight returns the element's layout width and height, while getBoundingClientRect() returns the rendering width and height.

What is offset width?

Typically, offsetWidth is a measurement in pixels of the element's CSS width, including any borders, padding, and vertical scrollbars (if rendered). It does not include the width of pseudo-elements such as ::before or ::after . If the element is hidden (for example, by setting style.


1 Answers

The Problem

It seems that sometimes the JavaScript code that calculates the width is executed before the web page is ready and the layout is calculated; I think that not setting the width property in the CSS file causes this issue.

Solution

So the solution is to wrap the JavaScript code in an event listener on window that listens for "load" event:

addEventListener("load", function () {
    var resumeHeader = document.querySelector('#resume-header');
    var resumeHeaderComputedWidth = getComputedStyle(resumeHeader, null).getPropertyValue('width');
    console.log(resumeHeaderComputedWidth)
}

Safari Case

However, this doesn't work with Safari due to its way of loading and displaying web pages. To work around this, add this line of code before the event listener described above:

window.scrollBy(0,1);

More info on this behavior of Safari and its workaround: Here.

Working Code

Here is the code with the above fixes:

window.scrollBy(0,1);
addEventListener("load", function () {
    var resumeHeader = document.querySelector('#resume-header');
    var resumeHeaderComputedWidth = getComputedStyle(resumeHeader, null).getPropertyValue('width');
    console.log(resumeHeaderComputedWidth);
    console.log(resumeHeader.offsetWidth);
    console.log(resumeHeader.clientWidth);
    console.log(resumeHeader.getBoundingClientRect().width);
});
#resume-header {
        display: inline-block;
        background-color: #F5D061;
        padding: 2px 6px;
        font-weight: bold;
        font-size: 14px;
        position: relative;
        top: 25px;
        left: 0;
        transition: width 1s ease;
        font-family: "Merriweather", serif;
        line-height: 1.8;
        letter-spacing: 1px;
        box-sizing: border-box;
}
<link href="https://fonts.googleapis.com/css?family=Merriweather:300,400" rel="stylesheet">
<div id="resume-header">
    Resume
</div>

Note: This solution has been tried on Chrome, Safari, and Chrome for mobile.

like image 94
Ammar Alyousfi Avatar answered Sep 22 '22 18:09

Ammar Alyousfi