1

While trying to make a basic collapsing div with Javascript, I came across the following odd bug; with this code:

<html> 
    <head> 
        <style type="text/css">
            .hidden
            {
                display: none;
            }
        </style>
        <script type="text/javascript">
            function toggle()
            {
                var element = document.getElementById('hidden1');
                if(element.style.display === 'none')
                {
                    element.style.display = 'inline';
                }
                else
                {
                    element.style.display = 'none';
                }
            }
        </script> 
    </head> 
    <body>
        <a onclick="toggle()">Click me</a><br /> 
        <div id="hidden1" class="hidden">
            stuffs
        </div>
    </body>
</html>

I had expected it to work as it would appear: the div would start hidden, and upon every click it would go from visible to invisible, however it does not: it requires 2 clicks to show the element the first time, and after some debugging I realised that the div seems to be in some weird quantuum flux. When adding the following alert to the top of the toggle() method:

alert('\'' + document.getElementById('hidden1').style.display + '\'');

I'm given '', meaning it's empty. However when I do this in exactly the same place:

console.log(document.getElementById('hidden1').style);

The console then shows:

[object CSS2Properties]

With the following properties:

0: "display"
...
display: "none"

However, when the CSS declaration for .hidden is moved inline to the div's style, the code works exactly as one would expect. Does anyone know why this is happening?

I'd also like to know if anyone can't reproduce the problem, as I've experienced it with apache on both Centos and Windows, and with Firefox 24, Chrome 30 and the latest Opera browsers.

MrLore
  • 3,759
  • 2
  • 28
  • 36
  • Hint: If you move your style inline, ` – user229044 Oct 18 '13 at 18:30

4 Answers4

7

That's the C (cascade) in CSS. You are checking if that element directly has the display property set to none. When you load your page, it does not. When you click it the first time, it checks if it's own style.display is none, it's undefined though; so it assigns "none" based on your if/else.

Now, when you click it again, it does have style.display set to "none", so it then changes it to "inline". The element still has the class .hidden which is set to have display: none, but because of the way CSS inherits properties, the element's setting is more valuable.

There are two ways to fix your problem. You can either add the display none inline like this

<div id="hidden1" style="display:none">

Or you can have your if else check for an undefined value and it equaling 'none' like this

// In function toggle()
 if(!element.style.display || element.style.display === 'none')

Which will first check for an empty string or undefined value, and also check if it's set to none.

You could actually do both of these, but it's better practice to not having styles inline if possible.

Will
  • 2,604
  • 2
  • 18
  • 14
  • `You are checking if that element directly has the display property set to none.` So you're saying that Javascript can only see inline declarations of CSS? – MrLore Oct 18 '13 at 18:36
  • 1
    Your browser has an order of how to apply styles (See something like this http://www.w3.org/TR/CSS2/cascade.html for more on that). You could use getComputedStyle https://developer.mozilla.org/en-US/docs/Web/API/Window.getComputedStyle to determine what the end result of the cascade is. Or you could logic something like "If this element has the `hidden` class, do X, otherwise, Y" and then instead of in your toggle function setting the elements local `style.display` you could instead add and remove the `hidden` class – Will Oct 18 '13 at 18:41
0

The problem is you're setting the style in two different ways. You've added a class, hidden, to the div that hides it, but you're also manually setting the CSS property with Javascript. The two conflict, causing the weird "quantum" behavior you're seeing.

I recommend either using jQuery to toggle visibility (you can do this with a single function) or, if you don't want to use jQuery, follow the directions in the first answer here.

Community
  • 1
  • 1
Christian Ternus
  • 8,406
  • 24
  • 39
  • But the CSS is changed within the function, and `display` element is blank even if the function is never called. – MrLore Oct 18 '13 at 18:32
0

.css styling and in-line/javascript styling apparently live in different dimensions. That's really weird.

Basically, style.display in javascript only knows of the values you set, not the ones your .css file sets..

Here's a quick work around: http://jsfiddle.net/qy3w8/

if (element.style.display == "") {
    element.style.display = "none";
}

Just added that if statement on top of your if statements. Or you could use jQuery, either works fine.

Scott Rowell
  • 1,135
  • 6
  • 8
0

Another option is to remove the style .hidden, and call your function toggle() on document load.

Thomas Murphy
  • 1,360
  • 4
  • 14
  • 41