0

I have a section of code that is supposed to toggle an object's height to grow when clicked, and if clicked again it shrinks to it's original height. I tried two methods to get this to work. The second method does work, while the first doesn't work repeatedly.

As I understand it, when I set a height in Javascript it effectively adds it inline on the "style" attribute in HTML, and as such overrules the CSS stylesheet height setting, and the object grows.

However, the first method only works once, and I don't know why. In Method 1, the object grows when clicked, shrinks on the second click, and then doesn't work again.

The second method grows on the first click using the same methodology as Method 1, however, to shrink it simply removes the styling that was added. And it works over and over again without issue.

Why doesn't Method 1 work over and over again, while Method 2 does?

Method 1

if(document.getElementById("block1").style.height < 500) {
    document.getElementById("block1").style.height = "500px";
}
else {
    document.getElementById("block1").style.height = "200px";
}

Method 2

if(document.getElementById("block1").style.height < 500) {
    document.getElementById("block1").style.height = "500px";
}
else {
    document.getElementById("block1").style.height = "";
}
CdnXxRRODxX
  • 341
  • 5
  • 14
  • 2
    Try `console.log( "200px" < 500 )` and see what you get. – JJJ Mar 07 '15 at 21:18
  • Just wrap the `document.getElementById("block1").style.height` in `parseInt()`. – mash Mar 07 '15 at 21:18
  • You may want to do `if(parseInt(document.getElementById("block1").style.height) < 500)` because you are comparing a string to an integer, which doesn't work. `parseInt("200px")` will give you `200` – vcapra1 Mar 07 '15 at 21:19
  • Oh alright, I was definitely looking too far into this. Why would it be an integer for the first time however? I then set it with a string, but wouldn't the command detect the height pixel count as an integer? Or does the browser just accept string or integer value? – CdnXxRRODxX Mar 07 '15 at 21:29
  • Actually I'm noticing with adding the ParseInt, it doesn't shrink or grow for the first click...then works after that repeatedly. – CdnXxRRODxX Mar 07 '15 at 21:30
  • you could use .offsetHeight instead of .style.height – maioman Mar 07 '15 at 21:37
  • For the record, the second method wouldn't work either if JavaScript's unary ``+`` operator didn't convert an empty string to ``0``. – Patrick Roberts Mar 07 '15 at 21:40
  • @ CdnXxRRODxX The reason parseInt doesn't work as expected the first time is that parseInt('') returns NaN, so you would need to check for the empty string. Personally, I agree with Oriol's answer that your approach is flawed, and there are better ways to check an element's height without resorting to type juggling. – Jonathan Nicol Mar 07 '15 at 22:17
  • *"Why would it be an integer for the first time however?"* – it isn't. It's an empty string and `"" < 500` == true. – JJJ Mar 07 '15 at 23:11

5 Answers5

2

As I understand it, when I set a height in Javascript it effectively adds it inline on the "style" attribute in HTML, and as such overrules the CSS stylesheet height setting, and the object grows.

Not necessarily. It's true that a common way of setting styles with JS is using style. But there are other ways, such as creating new stylesheets.

And inline styles don't always override styles in stylesheets. For example, an !important style in the stylesheet overrides a non-!important inline style.

Therefore, in most cases it's better to use getComputedStyle in order to get the computed style instead of the inline one.

But in this case there is a more direct approach: use clientHeight or offsetHeight.

Those return the number of pixels of the height of the element. Since it's a number, and not a string, you don't have to parse it in order to compare.

Therefore, you can use

var el = document.getElementById("block1");
el.style.height = el.clientHeight < 500 ? "500px" : "200px";

Note: if you want to be sure your style will be applied, you can set it as important. See how.

Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
1

When comparing a string with a number, JavaScript casts the string as a number or to NaN if it can't be cast.

Assume document.getElementById("block1").style.height is "200px".

"200px" cannot be cast to a number without using a function such as parseInt. Therefore, it becomes NaN in this comparison:

if(document.getElementById("block1").style.height < 500) {

NaN compared to anything (including itself!) is false.

Therefore, the else is always run in Method 1 when the style has been assigned a height:

if(document.getElementById("block1").style.height < 500) {
  document.getElementById("block1").style.height = "500px";
}
else {
  document.getElementById("block1").style.height = "200px";
}

That's why Method 1 works only once.

On the other hand, Method 2 always works, because the null string "" can be cast to a number – the number 0.

So the first time it runs, it's comparing NaN < 500 (false), and the second time it's comparing 0 < 500 (true).

The solution (as others have pointed out) is to compare the element's offsetHeight instead of style.height.

Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
  • Whew, you had me questioning myself. I had just finished reading the "Expressions and Operators" chapter in JavaScript: The Definitive Guide. – Rick Hitchcock Mar 07 '15 at 21:58
0

style.height returns a string value, but you are comparing it with an integer. Essentially your comparison looks like this:

if("200px" < 500)

When comparing a number and a string, the string is converted to a number, and the result of Number("200px") is NaN, so your comparison actually looks like this:

if(Nan < 500)

which returns false.

The reason your comparison works the first time is because initially style.height returns an empty string, since you haven't yet set the element's height. In this case '' is converted to 0, so you're checking if 0 is less than 500, which it is.

If you want to compare e.g. "200px" with an integer you can convert it to to an integer first using parseInt:

if(parseInt(document.getElementById("block1").style.height, 10) < 500)
Jonathan Nicol
  • 3,158
  • 1
  • 21
  • 22
  • Thanks for the response, I understand the string to integer comparison problem now. However, why would it work the first time at all then? – CdnXxRRODxX Mar 07 '15 at 21:32
  • 1
    I think this happens because the first time there is no inline style set, so style.height returns an empty value (zero). Once you set an inline style, it returns that instead. – Jonathan Nicol Mar 07 '15 at 21:35
  • I've edited my answer to clarify why the comparison works the first time only. – Jonathan Nicol Mar 07 '15 at 21:50
0

Maybe you have your reasons but this approach smells weird. Have you considered toggling the height by adding/removing a CSS class? That way you wouldn't need to deal with this. (And it also feels like more "proper" aproach.)

Why I think what you are doing is wrong:

  • you are sortof hiding "isShrinked" flag into a style property,
  • you are converting a dimension value into a number by dropping the "px" as if you could guarantee no other extension/script will change it to a dimension with different units,
  • you are completely ignoring the concept of CSS classes (this scenario is pretty much a book example of CSS-class usage)

I firmly believe you should add/remove a className on that element.

Jaroslav Záruba
  • 4,694
  • 5
  • 39
  • 58
  • @PaparazzoKid I considered that and I am ready to face replies like yours or even downvotes. What OP is doing is IMO wrong on several levels: – Jaroslav Záruba Mar 07 '15 at 21:57
  • I agree that adding/removing classes is preferable in most cases, but that doesn't solve the problem of how to check the element's height in the first place which is really what the question is about. – Jonathan Nicol Mar 07 '15 at 22:20
  • Thanks for the reponse! My methodology definitely isn't the best, I'll most likely make multiple classes and will be applying and removing those classes accordingly. @JonathanNicol was correct in assuming that I ultimately wanted to better understand how to check the element's height in the first place...which I'll be able to apply further along in the project I'm working on. – CdnXxRRODxX Mar 08 '15 at 23:21
  • Sorry for being annoying **** but you should not be deciding whether to collapse or expand _based on current height of the element_... There's multiple ways how that height may change "while you're not looking", and most importantly: when demands on the layout change (also you need to deal with several kinds of devices these days) you will have to change the JS code, that is bad ju-ju. :) Separate the looks form the logics is one of the most fundamental rules of coding. – Jaroslav Záruba Mar 09 '15 at 09:19
0

HTML/CSS

<span id="test1" style="color: #777;">test</span>

JavaScript

console.log(document.getElementById('test1').style.backgroundColor);
//""

console.log(document.getElementById('test1').style['background-color']);
//""

console.log(document.getElementById('test1').style.color);
//"rgb(119, 119, 119)"

console.log(document.getElementById('test1').style['color']);
//"rgb(119, 119, 119)"

Note hex values are incorrectly returned as RGB values.

John
  • 1
  • 13
  • 98
  • 177