Monday, September 22, 2014

When Computed Styles aren't Computed

I noticed something unexpected the other day when working with jQuery to read CSS dimension properties. Maybe everyone else in the world is already aware of this but I'll talk about it anyway.

Typically, whenever you use jQuery to read the value of a height or width property of an element, the value is always returned in pixels. You can set the value in %, px, em, cm, whatever. But it always comes back to you in pixels. It will actually be a string with "px" at the end.

Except when it's not.

I was relying on this behavior. I would grab the value, remove the "px" from the end, and turn it into a Number. But sometimes I was getting "NaN" as my value. Upon closer inspection, it turned out that I was getting a value like "82%" instead of "435px" which was throwing everything off.

Technically, jQuery is giving me what is known as the computed value of the property. Sure, sometimes getting the value in pixels is not what you want, but as long as it's consistent you can deal with it. I was encountering the problem that sometimes it was in pixels and sometimes it was the value that I set it from, which happened to be percent.

It took me a while, but I finally figured out what was going on.

When the element's parent (not the element itself) has a display value of "none," the browser can't give me the computed dimensions because it has no way to compute them. Instead, you get whatever value the CSS says.

Check out this test page I put together to demonstrate the issue.

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title>CSS Test</title>
</head>

<body>

  <div id="container" style="width: 400px;">
    <div id="test" style="width: 50%">test</div>
  </div>

</body>

<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script>

$(function() {

    console.log($('#test').css('width'));

    $('#container').toggle();

    console.log($('#test').css('width'));

    $('#container').toggle();

    console.log($('#test').css('width'));

});

</script>

</html>

Here is the console output.


As you can see, the test div has a width specified as 50% but when the value of the "width" CSS property is printed out, it is "200px". This is exactly correct, since 200px is 50% of the 400px width specified on the parent container.

After I toggle the visibility of the parent container (the jQuery .toggle() function) and print the width of the test div again, I get a value of "50%". That's what the style attribute says.

Toggle the parent container visibility again (make it visible) and you see "200px" on the console again.

Once I learned this and restored my sanity, I was able to fix my problem easily by making my elements' parent visible before I tried to read the dimensions. Originally I was trying to read them, perform some calculations, and then make the parent visible. There were no real issues with switching the order around a little in my case, but your mileage may vary.

Amphibian.com comic for September 22, 2014