No error or even warning, but my subLevel won't open & close

Hi,

I have a navMenu of which some items should open & close straight down, onclick. Because of that straight down opening, I thought it would be semantic enough if I would not put those items in a sub menu, but leave them in the main menu.

But although I do not get any error or even just a warning in IE9 (with debugger) and FF7 (Firebug 1.8.4), the subLevel items won’t open or close.

The code contains quite a lot of seemingly irrelevant matters, but this way you will have nicer menu to play with :). And it is a one-action copy & paste code.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>TEST</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
#menuContainer {
    width: 148px;
    position: absolute;
    top: 120px;
    bottom: 10px;
    }
#menuDiv {
    width: 108px;
    position: relative; 
    left: 21px;
    margin-top: 20px;
    }
#menuDiv ul {
    list-style: none;
    margin: 0;
    padding: 0;
    }
#menuDiv li {
    border: 1px solid #555;
    border-top: 0;
    white-space: nowrap;
    text-align: left;
    }
#menuDiv li#firstKid {
    border-top: 1px solid #666;
    }
#menuDiv li a {
    display: block;
    padding: 3px;
    font: normal 12px/18px Verdana; /* without line-height gaps in IE6 */
    color: #000;
    text-decoration: none;
    background-color:  #60b0b0;
    }
#menuDiv li a:hover {
    background-color: #000;
    color: #fff;
    }
#menuDiv a#hasSubLevel {
    background: #60b0b0 url(images/vakje_plus.gif) no-repeat 95% 63%;
    }
#menuDiv a#hasSubLevel:hover {
    background-color: #999;
    color: #000;
    }
#menuDiv li.subLevel {
    display: none;
    }
#menuDiv li.subLevel a {
    background-color: #ffff99;
    color: #000;
    }
#menuDiv li.subLevel a:hover {
    color: #fff;
    background: #000;
    }
#menuDiv li a.newWindow {
    background: #60b0b0 url('images/newWindow3.gif') no-repeat 95% 50%;
    }
#menuDiv li.subLevel a.newWindow {
    background: #ffff99 url('images/newWindow3.gif') no-repeat 95% 50%;
    }
#menuDiv li a.newWindow:hover, #menuDiv li.subLevel a.newWindow:hover {
    background: #000 url('images/newWindow3Neg.gif') no-repeat 95% 50%;
    color: white;
    }
</style>

<script type="text/javascript">
document.getElementsByClassName = function(cl)
{
    var retnode = [];
    var myclass = new RegExp('\\\\b'+cl+'\\\\b');
    var elem = this.getElementsByTagName('*');
    for (var i=0; i<elem.length; i++)
    {
        var classes = elem[i].className;
        if (myclass.test(classes)) retnode.push(elem[i]);
    }
        return retnode;
}
</script>
<script type="text/javascript">
function openSubLevel()
{
    var parent = document.getElementById('hasSubLevel');
    var children = document.getElementsByClassName('subLevel');
    for (var c=0; c<children.length; c++)
    {
        if (children[c].style.display == 'none')
        {
            children[c].style.display = 'block';
            parent.style.backgroundImage = 'url(images/vakje_min.gif)';
        }
        else return;
    }
}

function closeSubLevel()
{
    var parent = document.getElementById('hasSubLevel');
    var children = document.getElementsByClassName('subLevel');
    for (var n=0; n<children.length; n++)
    {
        if (children[n].style.display == 'block')
        {
            children[n].style.display = 'none';
            parent.style.backgroundImage = 'url(images/vakje_plus.gif)';
        }
        else return;
    }
}
</script>

</head>
<body>
        <div id="menuDiv">
            <ul>
                <li id="firstKid"><a href="#" onclick="closeSubLevel()">Introductie</a></li>
                <li><a href="#" onclick="closeSubLevel()">Kwaliteitseisen</a></li>
                <li><a href="#" onclick="closeSubLevel()">Zelf updaten</a></li>
                <li><a href="#" onclick="closeSubLevel()">Showcase</a></li>
                <li><a href="#" onclick="closeSubLevel()">Bedrijfsinfo</a></li>
                <li><a href="#" id="hasSubLevel" onclick="openSubLevel();">Werkwijze</a></li>
                <!-- begin subLevel -->
                    <li class="subLevel"><a href="layouts-overzicht.html" class="newWindow" target="_blank">Lay-outs</a></li>
                    <li class="subLevel"><a href="stylingmodule.php" class="newWindow" target="_blank">Styling</a></li>
                    <li class="subLevel"><a href="lettertypen.html">Lettertypen</a></li>
                    <li class="subLevel"><a href="fototips.html">Fototips</a></li>
                <!-- end subLevel -->
                <li><a href="#" class="newWindow" target="_blank">Mobiel</a></li>
            </ul>
        </div>
</body>
</html>

Thanks in advance.

Ancillary problem, maybe what’s actually going wrong for you: When the user clicks on the link, there’s no JS that disables the default action. So nothing happens, and the page reloads. The quick solution is to change the onclick handler to also return false:


<a ... onclick="openSubLevel();return false;">

Now, the real problem is that in your loop, you’re checking the style.display property of each child:


if (children[c].style.display == 'none')
{
    // ...
}
else return;

But you’re only setting the display to “none” in the CSS. And the JS style object doesn’t reflect stylesheet styles! So the first time you check the style.display on the first child, it’s going to be false!

There is no fix as easy as the “return false” thing from above. If it were me, I’d combine the openSubLevel and closeSubLevel functions into one. Then, instead of checking for a display of none, check for a display of block. Then, when it fails the first time because style.display returns undefined, it’s still doing the right action:


function openCloseSubLevel() {
    var parent = document.getElementById('hasSubLevel');
    var children = document.getElementsByClassName('subLevel');
    
    var currentlyShowing = children[0].style.display === 'block';
    var newDisplay = currentlyShowing ? 'none' : 'block';
    var newBackground = currentlyShowing ? 'plus' : 'min';
    newBackground = 'url(images/vakje_' + newBackground + '.gif)';

    for (var c = 0; c<children.length; c++) {
        children[c].style.display = newDisplay;
        children[c].style.backgroundImage = newBackground;
    }
}

[COLOR=#ff0000]if (children[n].style.display [/COLOR]== 'block')

The above notation cannot be used to check what the element’s css rule is.

There’s lots of examples on Google on how to get the style rules from the css using javascript.

For debugging purposes do an

alert([COLOR=#ff0000]children[n].style.display[/COLOR]);

and see what the output is.

Now that’s a speedy reply, for a Friday night! :slight_smile: Unfortunately however, inserting return false; doesn’t do the trick. For your convenience, I’m reposting the code, in which the only I changed is the insertion (and the correct code tags around it):


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>TEST</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
#menuContainer {
    width: 148px;
    position: absolute;
    top: 120px;
    bottom: 10px;
    }
#menuDiv {
    width: 108px;
    position: relative; /* margin:auto centreert hier niet goed */
    left: 21px;
    margin-top: 20px;
    }
#menuDiv ul {
    list-style: none;
    margin: 0;
    padding: 0;
    }
#menuDiv li {
    border: 1px solid #555;
    border-top: 0;
    white-space: nowrap;
    text-align: left;
    }
#menuDiv li#firstKid {
    border-top: 1px solid #666;
    }
#menuDiv li a {
    display: block;
    padding: 3px;
    font: normal 12px/18px Verdana; /* zonder line-height 'gaps' in IE6 */
    color: #000;
    text-decoration: none;
    background-color:  #60b0b0;
    }
#menuDiv li a:hover {
    background-color: #000;
    color: #fff;
    }
#menuDiv a#hasSubLevel {
    background: #60b0b0 url(images/vakje_plus.gif) no-repeat 95% 63%;
    }
#menuDiv a#hasSubLevel:hover {
    background-color: #999;
    color: #000;
    }
#menuDiv li.subLevel {
    display: none;
    }
#menuDiv li.subLevel a {
    background-color: #ffff99;
    color: #000;
    }
#menuDiv li.subLevel a:hover {
    color: #fff;
    background: #000;
    }
#menuDiv li a.newWindow {
    background: #60b0b0 url('images/newWindow3.gif') no-repeat 95% 50%;
    }
#menuDiv li.subLevel a.newWindow {
    background: #ffff99 url('images/newWindow3.gif') no-repeat 95% 50%;
    }
#menuDiv li a.newWindow:hover, #menuDiv li.subLevel a.newWindow:hover {
    background: #000 url('images/newWindow3Neg.gif') no-repeat 95% 50%;
    color: white;
    }
</style>

<script type="text/javascript">
/*<![CDATA[*/
document.getElementsByClassName = function(cl)
{
    var retnode = [];
    var myclass = new RegExp('\\\\b'+cl+'\\\\b');
    var elem = this.getElementsByTagName('*');
    for (var i=0; i<elem.length; i++)
    {
        var classes = elem[i].className;
        if (myclass.test(classes)) retnode.push(elem[i]);
    }
        return retnode;
}
/*]]>*/
</script>
<script type="text/javascript">
/*<![CDATA[*/
function openSubLevel()
{
    var parent = document.getElementById('hasSubLevel');
    var children = document.getElementsByClassName('subLevel');
    for (var c=0; c<children.length; c++)
    {
        if (children[c].style.display == 'none')
        {
            children[c].style.display = 'block';
            parent.style.backgroundImage = 'url(images/vakje_min.gif)';
        }
        else return;
    }
}

function closeSubLevel()
{
    var parent = document.getElementById('hasSubLevel');
    var children = document.getElementsByClassName('subLevel');
    for (var n=0; n<children.length; n++)
    {
        if (children[n].style.display == 'block')
        {
            children[n].style.display = 'none';
            parent.style.backgroundImage = 'url(images/vakje_plus.gif)';
        }
        else return;
    }
}
/*]]>*/
</script>

</head>
<body>
        <div id="menuDiv">
            <ul>
                <li id="firstKid"><a href="#" onclick="closeSubLevel();return false;">Introductie</a></li>
                <li><a href="#" onclick="closeSubLevel();return false;">Kwaliteitseisen</a></li>
                <li><a href="#" onclick="closeSubLevel();return false;">Zelf updaten</a></li>
                <li><a href="#" onclick="closeSubLevel();return false;">Showcase</a></li>
                <li><a href="#" onclick="closeSubLevel();return false;">Bedrijfsinfo</a></li>
                <li><a href="#" id="hasSubLevel" onclick="openSubLevel();return false;">Werkwijze</a></li>
                <!-- begin subLevel -->
                    <li class="subLevel"><a href="layouts-overzicht.html" class="newWindow" target="_blank">Lay-outs</a></li>
                    <li class="subLevel"><a href="stylingmodule.php" class="newWindow" target="_blank">Styling</a></li>
                    <li class="subLevel"><a href="lettertypen.html">Lettertypen</a></li>
                    <li class="subLevel"><a href="fototips.html">Fototips</a></li>
                <!-- eind subLevel -->
                <li><a href="#" class="newWindow" target="_blank">Mobiel</a></li>
            </ul>
        </div>
</body>
</html>

Regarding combining the two functions: I deliberately didn’t do that because I only want the subLevel to open when the ‘parent item’ is clicked, and only closed when the other main menu items are clicked.

You still have


[COLOR=#000080]if (children[n].style.display == 'block')[/COLOR]

in your code which is incorrect.

See my previous post.

Bear in mind that just because you don’t get any errors or warning, that doesn’t mean there is nothing wrong with your code.

No errors/warnings just means there are no syntax errors in your code, nothing more. If the output is still not what you want then that means you have logic errors in your code.

A really simple example

var a = 2 * 3;

if(a > 10){
     alert('got here');
}

No errors or warnings will be generated with the above code, but the alert() will never be triggered because of the logic error in that a is a constant and <= 10. Most debuggers will not detect that the alert() will never be triggered.

So if your code is not working, you need to do some basic debugging and step through your code and check values of variables using either a debugger or alert()'s. When a variable is not the expected value, then back track through your code to find the source of the error and fix it.

If you develop sound debugging skills you can save a lot of time in the future by not having to wait around in forums for people to help you fix what a lot of the times are simple problems that can be fixed with some basic debugging.

Hey Webdev, nice to see you again! Our posts crossed, and when I hit the ‘Post’ button I had to get something to eat and some sleep, so I’m only reading your replies now. Thanks for the alert debugging trick. I just solved the matter by taking the if lines out completely, but I still have a few questions.

If javascript cannot retrieve CSS values the way I thought it could, then why does this example work like a charm:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
#navMenuDiv #subLevel_1,
#navMenuDiv #subLevel_2 {
         display: none;
         }
</style>
<script type="text/javascript">
/*<![CDATA[*/
      function toggleDisplay(id)
      {
      var menuSubLevel = document.getElementById(id);
      if (menuSubLevel.style.display == "block")
      { menuSubLevel.style.display = "none"; }
      else
      { menuSubLevel.style.display = "block"; }
      }
/*]]>*/
</script>
</head>
<body>
  <div id="navMenuDiv">
    <ul>
      <li><a href="#">Home</a></li>
      <li><a href="#">Visie op Fitness</a></li>
      <li><a href="#" onclick="toggleDisplay('subLevel_1')">Activiteiten &darr;</a>
              <ul id="subLevel_1">
                <li><a href="#">Bodybuilding</a></li>
                <li><a href="#">Bodyshaping</a></li>
                <li><a href="#">Conditietraining</a></li>
                <li><a href="#">Groepsactiviteiten</a></li>
                <li><a href="#">Yoga</a></li>
                <li><a href="#">Zelfverdediging</a></li>
                <li><a href="#">Speciale uren</a></li>
                <li><a href="#">Weight Watching</a></li>
              </ul>
      </li>
      <li><a href="#" onclick="toggleDisplay('subLevel_2')">Faciliteiten &darr;</a>
              <ul id="subLevel_2">
                <li><a href="#">Fitnessapparatuur</a></li>
                <li><a href="#">Sauna</a></li>
                <li><a href="#">Bruinen</a></li>
                <li><a href="#">Massage</a></li>
                <li><a href="#">Personal coaching</a></li>
                <li><a href="#">Kinderopvang</a></li>
              </ul>
      </li>
      <li><a href="#">Tarieven</a></li>
      <li><a href="#">Acties</a></li>
      <li><a href="#">Locatie</a></li>
      <li><a href="#">Verdere info</a></li>
      <li><a href="#">Mobiel</a></li>
    </ul>
  </div>
</body>
</html>

The second question is: do you know a good javascript course for intermediate students? This issue wasn’t mentioned in the W3 Schools beginners course, including the DHTML course.

Thanks in advance. (I still owe you a reporting back on that particular increment/decrement question, and I will report back.)

I didn’t say it can’t. What I said is, it can’t the way you were doing it. This tute might help you understand.

Read through sdleihssirhc’s post where (s)he explains

And the JS style object doesn’t reflect stylesheet styles! So the first time you check the style.display on the first child, it’s going to be false!

There are plenty of examples on the www showing how to extract the css rules from stylesheets with javascript.

And the JS style object doesn’t reflect stylesheet styles! So the first time you check the style.display on the first child, it’s going to be false!

I read that, but my brain seemingly couldn’t accept that because of the last example I gave, in which the style block values were read out by the Javascript. Also because (s)he gave another tip, a quick fix, I wanted to try that first.

This tute might help you understand.

Yes and no. I do get the concept now of javascript in principle not being able to read out style blocks and sheets, but the author doesn’t explain why my last example does work. Why are ID style declarations returned, and class style declarations not?

Lets go a little more in depth on this; say you start with the following HTML:


<p id="test">hello, world</p>

And you use the following CSS:


#test { display:none }

Now (or, once the DOM has loaded) try the following script:


var elem = document.getElementById('test');
alert(elem.style.display); // alerts "undefined"

So when you have the following if-else check…


if (elem.style.display === 'block') {
    elem.style.display = 'none';
} else {
    elem.style.display = 'block';
}

…it looks like everything’s working. But the first time through, you’re comparing undefined to “block”. So you get the outcome you were expecting, but not for the reason you thought.

OK, my explanation that it depends on the use of an ID versus a class is not correct. I just changed my last example to getElementsByClassName, and then it works just as well.

But your explanation that Javascript doesn’t reflect style sheet (i.e. block) declarations, if that is what you are saying, is not correct either. Otherwise my last example wouldn’t work – I’m not using inline styles.

If I understand your last message correctly, it would all about the first-time comparison. And indeed, if I swap “block” and “none” in the Javascript in my last example, I have to click twice for the sub menu to appear, and then furthermore just once. And if I swap “block” and “none” in the Javascript in my first example, it does work.

So it seems as if the first-time comparison has to check a condition that is opposite of what is declared in the style block. I could very well live with that, but if I click the ‘parent link’ Werkwijze in my first example more than once, nothing happens. Why is that then, considering the successful two-click action described in the above paragraph?

Also, why is it that the use of inline styles solves all the problems? I don’t have to swap anything in my first example, just apply inline styles, and everything works. So apparently the algorithm is much more complicated then just the first-time comparison?

It is correct unless you first assign the style with javascript.

The simple alert() sdleihssirhc showed you clearly shows how.


[COLOR=#000066]alert[/COLOR][COLOR=#009900]([/COLOR]elem.[COLOR=#660066]style[/COLOR].[COLOR=#660066]display[/COLOR][COLOR=#009900])[/COLOR][COLOR=#339933];[/COLOR] [COLOR=#006600][I]// alerts "undefined"[/I][/COLOR]

Your code probably works, but not because of the reason you think :slight_smile:

So the thesis is: “Javascript doesn’t reflect style block or style sheet declarations, unless you first assign the style with javascript.”? But why does my second example (third message) work then? How did I assign the style with javascript there, which I did not do or do incorrectly in the first example?

When JavaScript sets and retrieves styles, they are the inline styles on the element that it works with. Those inline styles override stylesheet styles that may also affect the same element.
Normally when working with HTML we stay far away from using inline styles on an element, and use an external stylesheet for styles instead.

Paul,

I’m not using any inline styles in my examples. Yet, my second example (third message) works. So the question remains: Why does my second example (third message) work? How did I assign the style with javascript there, that I did not do or do incorrectly in my first example?

The third message is by webdev1958. Please check again?

Third message = my third message.

#3 is the message that was posted by webdev1958. Can you please link to the one that you mean?

my third message

Ahh, the third posting that you have made to this thread, not the third message of this thread.

Okay, the code in that post seems to work. What was the problem, or the question you had in regard to it?