2

I've just begun to learn JavaScript.

I wrote a simple dropdown menu, but when you initially load the page, two clicks on the "dropdown"- button (with onclick attribute) are required for the link list to be displayed.

After that, it works as intended - you only need to click one time on the button to display/hide the list.

But why do I have to click two times on the button after intially loading the site?

Here's my HTML/CSS:

#dropbtn {
    background-color: #4CAF50;
    color: white;
    padding: 16px;
    font-size: 16px;
    border: none;
    cursor: pointer;
}

#dropdown {
    position: relative ;
    display: inline-block;
}

#dropdown-content {
    display: none;
    position: absolute;
    background-color: #f9f9f9;
    min-width: 160px;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    z-index: 1;
}

#dropdown-content a {
    color: black;
    padding: 12px 16px;
    text-decoration: none;
    display: block;
}

#dropdown-content a:hover {background-color: #f1f1f1}

/*#dropdown:hover #dropdown-content {
    display: block;
}*/

#dropdown:hover #dropbtn {
    background-color: #3e8e41;
}
<h2>Dropdown Menu</h2>
<p>Just a simple menu with link list</p>

<div id="dropdown">
  <button id="dropbtn" onclick="myFunction()">Dropdown</button>
  <div id="dropdown-content">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<script>

function myFunction() {
if (document.getElementById('dropdown-content').style.display == "none") 
         {document.getElementById('dropdown-content').style.display = "block";}
else {document.getElementById('dropdown-content').style.display = "none";}
    
    
}

</script>
bodemiller1
  • 73
  • 2
  • 8
  • The answer below from Yamada is correct! You must have the declaration (display: none;) in the style sheet and inside the tag. – Cesar Devesa Apr 11 '22 at 21:34

7 Answers7

2

In the initial case value of display property would be undefined unless you are setting css property using inline style attribute, so toggle the if condition and code blocks.

function myFunction() {
  // cache the element for later use, which is one of the best practice 
  var ele = document.getElementById('dropdown-content');

  if (ele.style.display == "block") {
    ele.style.display = "none";
  } else {
    ele.style.display = "block";
  }
}

#dropbtn {
  background-color: #4CAF50;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

#dropdown {
  position: relative;
  display: inline-block;
}

#dropdown-content {
  display: none;
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

#dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

#dropdown-content a:hover {
  background-color: #f1f1f1
}


/*#dropdown:hover #dropdown-content {
    display: block;
}*/

#dropdown:hover #dropbtn {
  background-color: #3e8e41;
}
<h2>Dropdown Menu</h2>
<p>Just a simple menu with link list</p>

<div id="dropdown">
  <button id="dropbtn" onclick="myFunction()">Dropdown</button>
  <div id="dropdown-content">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<script>
  function myFunction() {
    // cache the element for later use 
    var ele = document.getElementById('dropdown-content');
    if (ele.style.display == "block") {
      ele.style.display = "none";
    } else {
      ele.style.display = "block";
    }
  }
</script>

Or set display property using inline style attribute.

<div id="dropdown-content" style="display:none">
 <a href="#">Link 1</a>
 <a href="#">Link 2</a>
 <a href="#">Link 3</a>
</div>

#dropbtn {
  background-color: #4CAF50;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

#dropdown {
  position: relative;
  display: inline-block;
}

#dropdown-content {
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

#dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

#dropdown-content a:hover {
  background-color: #f1f1f1
}


/*#dropdown:hover #dropdown-content {
    display: block;
}*/

#dropdown:hover #dropbtn {
  background-color: #3e8e41;
}
<h2>Dropdown Menu</h2>
<p>Just a simple menu with link list</p>

<div id="dropdown">
  <button id="dropbtn" onclick="myFunction()">Dropdown</button>
    <div id="dropdown-content" style="display:none">
     <a href="#">Link 1</a>
     <a href="#">Link 2</a>
     <a href="#">Link 3</a>
    </div>
</div>
<script>
  function myFunction() {
    // cache the element for later use 
    var ele = document.getElementById('dropdown-content');
    if (ele.style.display == "none") {
      ele.style.display = "block";
    } else {
      ele.style.display = "none";
    }
  }
</script>

Taken from MDN Docs :

The HTMLElement.style property is used to get as well as set the inline style of an element. While getting, it returns a CSSStyleDeclaration object that contains a list of all styles properties for that element with values assigned for the attributes that are defined in the element's inline style attribute. See the CSS Properties Reference for a list of the CSS properties accessible via style. The style property has the same (and highest) priority in the CSS cascade as an inline style declaration set via the style attribute.

Pranav C Balan
  • 113,687
  • 23
  • 165
  • 188
2

Actually your dropdown-content has no style within it at the beginning, it has a css associated to it. Just declare the display: none on the declaration and it will work as expected.

#dropbtn {
    background-color: #4CAF50;
    color: white;
    padding: 16px;
    font-size: 16px;
    border: none;
    cursor: pointer;
}

#dropdown {
    position: relative ;
    display: inline-block;
}

#dropdown-content {
    display: none;
    position: absolute;
    background-color: #f9f9f9;
    min-width: 160px;
    box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
    z-index: 1;
}

#dropdown-content a {
    color: black;
    padding: 12px 16px;
    text-decoration: none;
    display: block;
}

#dropdown-content a:hover {background-color: #f1f1f1}

/*#dropdown:hover #dropdown-content {
    display: block;
}*/

#dropdown:hover #dropbtn {
    background-color: #3e8e41;
}
<h2>Dropdown Menu</h2>
<p>Just a simple menu with link list</p>

<div id="dropdown">
  <button id="dropbtn" onclick="myFunction()">Dropdown</button>
  <div id="dropdown-content" style="display: none">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<script>

function myFunction() {
if (document.getElementById('dropdown-content').style.display == "none") 
         {document.getElementById('dropdown-content').style.display = "block";}
else {document.getElementById('dropdown-content').style.display = "none";}
    
    
}

</script>
Yamada
  • 723
  • 6
  • 23
1

The style property of the HTML element is not derived from the css styles applied to the element. So you're not accessing your #dropdown-content when you access style.display. Instead what you might do is have a css class that you add or removing depending on it's state.

Example adopted from your code:

#dropbtn {
  background-color: #4CAF50;
  color: white;
  padding: 16px;
  font-size: 16px;
  border: none;
  cursor: pointer;
}

#dropdown {
  position: relative;
  display: inline-block;
}

.hide {
  display: none;
}

#dropdown-content {
  position: absolute;
  background-color: #f9f9f9;
  min-width: 160px;
  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
  z-index: 1;
}

#dropdown-content a {
  color: black;
  padding: 12px 16px;
  text-decoration: none;
  display: block;
}

#dropdown-content a:hover {
  background-color: #f1f1f1
}


/*#dropdown:hover #dropdown-content {
    display: block;
}*/

#dropdown:hover #dropbtn {
  background-color: #3e8e41;
}
<h2>Dropdown Menu</h2>
<p>Just a simple menu with link list</p>

<div id="dropdown">
  <button id="dropbtn" onclick="myFunction()">Dropdown</button>
  <div id="dropdown-content" class="hide">
    <a href="#">Link 1</a>
    <a href="#">Link 2</a>
    <a href="#">Link 3</a>
  </div>
</div>
<script>
  function myFunction() {
    let element = document.getElementById('dropdown-content');
    if (element.classList.contains('hide')) {
      element.classList.remove('hide')
    } else {
      element.classList.add('hide')
    }


  }
</script>
E. Sundin
  • 4,103
  • 21
  • 30
0

Just replace none with block and block with none in the function, that's what you need.

Al.G.
  • 4,327
  • 6
  • 31
  • 56
K. P.
  • 540
  • 5
  • 17
0

@sdleihssirhc tells in another post how to check if a element is being displayed or not. check out this post.

return element.currentStyle ? element.currentStyle.display :
                          getComputedStyle(element, null).display;

This will give you the value of a styled element.

Dennis Spierenburg
  • 613
  • 2
  • 6
  • 16
0

I was having the same issue, basically the first click activates the event handler in Javascript and the second click is the one that actually works.

To work your way around this call the event handler as soon as the page loads in your HTML file:

<body onload="myFunction()">

This will also make the first click work when using a phone.

0

Another way you can do it, which involves Dennis Spierenburg answer is to change

    function myFunction() {
    if (document.getElementById('dropdown-content').style.display == "none") 
     {document.getElementById('dropdown-content').style.display = "block";}
    else {document.getElementById('dropdown-content').style.display = "none";}
    }

to

    function myFunction() {
      const myElement = document.getElementById('dropdown-content');
      const myElementDisplay = getComputedStyle(myElement);
      if (myElementDisplay.display === "none") {
        myElement.style.display = "block";
    }
      else {
        myElement.style.display = "none";
      }
    }