1

I'm new to JavaScript and I've ran into a problem: I've built a dropdown menu/navbar in my HTML page. When you click a button, it opens up like it should, but I'm struggling with closing it. It's supposed to close when you click the button again, but I can't do that for some reason.

My method was to assign a class btn and id disabled-btn to a div. After clicking and opening the dropdown, button is supposed to have a new id of enabled-btn, which is should be selected so I could reverse the process.

I understood via debugging that you can't query select an element if it's not on the page yet, but I haven't managed to bypass it anyhow.

This is one of the variants of code that I tried.

const disabledButton = document.querySelector('#disabled-btn')
const navbar = document.querySelector('.my_navbar')
const enabledButton = document.querySelector('#enabled-btn')
const button = document.querySelector('.btn')

disabledButton.onclick = () => {
  navbar.style.transform = 'translate(0%, 0%)'
  disabledButton.style.rotate = '90deg'
  disabledButton.setAttribute('id', 'enabled-btn')
  if (button === enabledButton) {
    enabledButton.onclick = () => {
      navbar.style.transform = 'translate (-100%, 0%)'
      enabledButton.style.rotate = '0deg'
      enabledButton.setAttribute('id', 'disabled-btn')
    }
  }
}
<div class="open-btn" id="disabled-btn">
  <svg ...some svg here...></svg>
</div>
<nav class="my_navbar">
  <div class="my_navbar-nav"><a href="#">GALLERY</a></div>
  <div class="my_navbar-nav"><a href="#">PRESETS</a></div>
  <div class="my_navbar-nav"><a href="#">ABOUT</a></div>
  <div class="my_navbar-nav"><a href="#">BOOKING</a></div>
</nav>
cloned
  • 6,346
  • 4
  • 26
  • 38
  • 1
    It would be helpful to see your HTML for this. The way you have set up your JS needs help, but easier if we can see the HTML structure – Kinglish Sep 12 '22 at 18:35
  • Your can't bypass this. This is not possible just to annoy people, it's just technically not possible. Don't switch id, just have one onclick. And handle the state via data attribute (or class or whatever) – cloned Sep 12 '22 at 18:36
  • @Kinglish thank you for your suggestion, I provided my HTML as well! I know my JS is definately far from perfect, but I just got started, so I kinda struggle with it at the moment. – Alexander_M Sep 12 '22 at 18:40
  • @cloned sorry, I didn't know it wasn't possible. I'm new to JS and it's also notorious for its complexity, so I guess I'm making a dumb mistake. – Alexander_M Sep 12 '22 at 18:42
  • I see the HTML, but not the enabled button – Kinglish Sep 12 '22 at 18:42
  • @Kinglish well, enabled button is supposed to be the same div, but the ID would be changed to "enabled-btn" via JS. – Alexander_M Sep 12 '22 at 18:43

2 Answers2

2

Don't change ID's. They are unique identifiers and thus not meant to change. Instead I recommend using attributes. Just make sure it doesn't exist as an attribute already. When in doubt, add data-attributename.

You will need to set event listeners to each item and deal with the logic inside of it. Here is an example with some comments on what is going on. I am not sure what you are trying to do styling wise, but I recommend just adding and removing classes, and deal with styles inside of CSS. This will make it conceptually more separate and easier to understand.

Let me know if this helps on what you are trying to achieve.

// Getting all the nav items in an a 'nodelist' 
const navItems = document.querySelectorAll('.navbar .nav-item')


// Adding an event listener to each nav item
navItems.forEach( item => {
  item.addEventListener('click', () => {
  
    // check if item has 'active' attribute
    if (item.hasAttribute('active')) {
      // remove if it does
      item.removeAttribute('active')
    } else {
      // otherwise add it
      item.setAttribute('active', '')
    }
  })
})
.navbar a {
  text-decoration: none;
  color: black;
}

.nav-item[active] a {
  text-decoration: underline;
  color: blue;
}
<nav class="navbar">
  <div class="nav-item" active>
    <a href="#">GALLERY</a>
  </div>
  <div class="nav-item">
    <a href="#">PRESETS</a></div>
  <div class="nav-item">
    <a href="#">ABOUT</a>
  </div>
  <div class="nav-item">
    <a href="#">BOOKING</a>
  </div>
</nav>
SaroGFX
  • 699
  • 6
  • 20
  • You are also not meant to use invalid HTML attributes like `active` . Better to use `data-attribute` for this. – cloned Sep 13 '22 at 06:16
  • @cloned please tell me, why not? is it a convention or is it easy to break something with attributes like `active`? – Alexander_M Sep 13 '22 at 06:26
  • By the way, is there a difference between `toggling` `active` attribute and manually checking and removing or adding it? – Alexander_M Sep 13 '22 at 06:31
  • It's 100% fine to use custom attributes, as long as they don't exist on the element. 'disabled' on an input for example, will actually disable it. The reason why @cloned says don't use it, it because it makes your HTML 'invalid'. But this doesn't mean anything. You can make it strict by adding some DOCTYPE, check this out: https://stackoverflow.com/questions/1735230/can-i-add-a-custom-attribute-to-an-html-tag I think using data- makes sense when you're storing data in it. But if you use data- always, you're going to be more safe convention wise. It's like putting ; after each line in JS – SaroGFX Sep 13 '22 at 07:55
  • @Alexander_M If you're only wanting to toggle there is not really a big difference with using toggleAttribute or toggleClass I think. But if you need some side-effects (like remove the active from all the other elements) it could be more useful to write it all out. – SaroGFX Sep 13 '22 at 08:06
  • See this SO question (and answer): https://stackoverflow.com/questions/29196622/html-active-attribute `It is not syntactically incorrect to specify arbitrary attributes on an element, but it does yield an invalid document.` My advice is to just use data-attribute to show that you have a basic understanding of HTML. – cloned Sep 13 '22 at 08:21
1

css:you dont have to make seperate buttons for enable and disable. here i gave some css to .my_navbar and seperate css when it has active class with it.my_navbar.active..

js:getting both nav and btn as usual with querySelector the i am putting eventListerner of typeclick on disabledButton & inside that function i am toggling the active on the navbar. hope it helped

/*const disabledButton = document.querySelector('#disabled-btn')
const navbar = document.querySelector('.my_navbar')
const enabledButton = document.querySelector('#enabled-btn')
const button = document.querySelector('.btn')

disabledButton.onclick = () => {
   navbar.style.transform = 'translate(0%, 0%)'
   disabledButton.style.rotate = '90deg'
   disabledButton.setAttribute('id', 'enabled-btn')
   if (button === enabledButton) {
        enabledButton.onclick = () => {
        navbar.style.transform = 'translate (-100%, 0%)'
        enabledButton.style.rotate = '0deg'
        enabledButton.setAttribute('id', 'disabled-btn')
    }
  }
}*/
const disabledButton = document.querySelector('#disabled-btn');
    const navbar = document.querySelector('.my_navbar');

    disabledButton.addEventListener("click", () => {
        navbar.classList.toggle("active")
    })
.my_navbar {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: flex-start;
        padding: 0px;
        gap: 10px;
        background-color: gray;
        height: 0;
        overflow: hidden;
        transition: all 0.25s ease-in-out;
    }

    .my_navbar.active {
        padding: 20px;
        height: 190px;
    }

    .my_navbar-nav {
        width: 90%;
        padding: 10px 30px;
        background-color: lightgray;
    }

    .my_navbar-nav>a {
        color: white;
        text-decoration: none;
    }
<div class="open-btn" id="disabled-btn">
        <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-menu-2" width="40" height="40"
            viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke- linecap="round"
            stroke-linejoin="round">
            <path stroke="none" d="M0 0h24v24H0z" fill="none" />
            <line x1="4" y1="6" x2="20" y2="6" />
            <line x1="4" y1="12" x2="20" y2="12" />
            <line x1="4" y1="18" x2="20" y2="18" />
        </svg>
    </div>
    <nav class="my_navbar">
        <div class="my_navbar-nav"><a href="#">GALLERY</a></div>
        <div class="my_navbar-nav"><a href="#">PRESETS</a></div>
        <div class="my_navbar-nav"><a href="#">ABOUT</a></div>
        <div class="my_navbar-nav"><a href="#">BOOKING</a></div>
    </nav>
UmairFarooq
  • 1,269
  • 1
  • 3
  • 8