Skip to main content

Problem with accordion's toggle when combining it with navtabs (Bootstrap)

So, I'm creating this navigation thing using Bootstrap which will be present in the form of tabs in desktop and for mobile it will be an accordion. It's almost finished except there's this error I am not able to resolve.

Problem: If I click on any of the tabs (say Link #3) in the desktop version and then I switch to mobile, the correct tab is opened (i.e. Link #3) BUT when I click on it to close the tab and hide the content even though the content gets hidden, the button and toggle remains in "open state". This problem can be seen in desktops or tablets (as they'll show the navtabs). To view the problem on desktop just decrease the viewport size after clicking the nav tab.

I thought that if I could use "classList.contains()" to somehow keep track of when the content has "show" class and when it does not I'd be able to control it. But, I think it only keeps track of the class if I mentioned the class inline (it keeps on alerting true for the first content element and false for the rest of them). If I don't have the class mentioned inline it doesn't keep track of it even though Bootstrap is internally toggling between those classes. Or maybe I'm using the contains function in wrong fashion. Idk.

I also thought that maybe if I can keep track of the clicks i.e. if navtabs are being clicked and then if accordion is being clicked then class ("collapse") should be removed or added from or to the accordion button. But this then disrupts the other situations in which the toggle is working correctly.

I'm also unable to use the toggle() for Bootstrap accordion's "collapse" or "show".

I couldn't figure out anything so here I am at last asking you to help me. Please help me resolve this issue.

EDIT: So, I have managed to get .contains() to work BUT then I need to add a recursive function to get the "collapsed" class to toggle which is making the page slower and eventually unresponsive.

Here's what I did:

  • added a click event (for accordion btn #2) that should be checked after Link #2 is clicked in navtab
  • if the accordion btn (Link #2) is clicked after clicking the navtab (Link #2), it should check if the content is visible, if it is then a recursive function btn2Toggle() is called. (I think I made a pretty terrible recursive function)

PS: I wouldn't have to do this if toggle() worked but it isn't working. Also, I feel like I'm making it more complex than it needs to be. Am I going right or am I making it more complex?

const btn1 = document.getElementById('btn-1');
const btn2 = document.getElementById('btn-2');
const btn3 = document.getElementById('btn-3');
const tab1 = document.getElementById('nl-1');
const tab2 = document.getElementById('nl-2');
const tab3 = document.getElementById('nl-3');
const content1 = document.getElementById('flush-collapseOne');
const content2 = document.getElementById('flush-collapseTwo');
const content3 = document.getElementById('flush-collapseThree');

// this function will add and remove the collapsed class from btn
function btn2Toggle() {
  btn2.addEventListener('click', function() {
    btn2.classList.add('collapsed');
    btn2.addEventListener('click', function() {
      btn2.classList.remove('collapsed');
      if (btn2.onclick) {
        btn2Toggle();
      }
      else {
        return;
      }
    })
  });
  // or I could simply have this line of code if toggle() worked
  // btn2.addEventListener('click', function() {
  //    btn2.classList.toggle('collapsed');
  //  })
}


function contentVisible() {
    if (content1.classList.contains("show")) {
      // alert('content 1 visible');
    };
    if (content2.classList.contains("show")) {
      // alert('content 2 visible');
      btn2Toggle();
    };
    if (content3.classList.contains("show")) {
      // alert('content 3 visible');
    };
  }


function callTab() {

  // if tab1 is clicked on desktop, show only the content associated with btn1, collapse the rest of them
  if (tab1.addEventListener('click', function() {
    btn2.classList.add('collapsed');
    btn3.classList.add('collapsed');
    btn1.classList.remove('collapsed');
  }));

  // if tab2 is clicked on desktop, show only the content associated with btn2, collapse the rest of them
  if (tab2.addEventListener('click', function() {
    btn1.classList.add('collapsed');
    btn3.classList.add('collapsed');
    btn2.classList.remove('collapsed');
    // if navtab #2 (i.e. Link #2) is clicked and then if accordion 
    // button #2 (i.e. Link #2) is clicked then contentVisible() 
    // will be called
      document.querySelectorAll('.accordion-button').addEventListener('click', contentVisible());
  }));

  // if tab3 is clicked on desktop, show only the content associated with btn3, collapse the rest of them
  if (tab3.addEventListener('click', function() {
    btn1.classList.add('collapsed');
    btn2.classList.add('collapsed');
    btn3.classList.remove('collapsed');
  }));

};


function myFunction(x) {
  if (x.matches) { // If media query matches
     document.querySelectorAll('.nav-link').addEventListener('click', callTab());
  }
}

var x = window.matchMedia("(min-width: 992px)")
myFunction(x) // Call listener function at run time
x.addListener(myFunction) // Attach listener function on state changes
.container .nav-tabs {
  display: none;
}

/* MEDIA QUERY */
@media screen and (min-width: 992px) {
  .container .nav-tabs {
    display: flex;
  }
  .accordion-item h2 {
    display: none;
  }
  .container .accordion-item {
    border: none;
  }
}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
    
  </head>
  <body>
    <div class="container">
      <ul class="nav nav-tabs">
        <li class="nav-item">
          <a class="nav-link active" id="nl-1" aria-current="page" href="#flush-collapseOne" data-bs-toggle="tab">Link #1</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" id="nl-2" href="#flush-collapseTwo" data-bs-toggle="tab">Link #2</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" id="nl-3" href="#flush-collapseThree" data-bs-toggle="tab">Link #3</a>
        </li>
      </ul>

      <div class="accordion accordion-flush" id="accordionFlushExample">
        <div class="accordion-item">
          <h2 class="accordion-header">
            <button id="btn-1" onclick="document.querySelector('#nl-1').click()" class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseOne" aria-expanded="false" aria-controls="flush-collapseOne">
              Link #1
            </button>
          </h2>
          <div id="flush-collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionFlushExample">
            <div class="accordion-body">content 1</div>
          </div>
        </div>
        <div class="accordion-item">
          <h2 class="accordion-header">
            <button id="btn-2" onclick="document.querySelector('#nl-2').click()" class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseTwo" aria-expanded="false" aria-controls="flush-collapseTwo">
              Link #2
            </button>
          </h2>
          <div id="flush-collapseTwo" class="accordion-collapse collapse" data-bs-parent="#accordionFlushExample">
            <div class="accordion-body">content 2</div>
          </div>
        </div>
        <div class="accordion-item">
          <h2 class="accordion-header">
            <button id="btn-3" onclick="document.querySelector('#nl-3').click()" class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#flush-collapseThree" aria-expanded="false" aria-controls="flush-collapseThree">
              Link #3
            </button>
          </h2>
          <div id="flush-collapseThree" class="accordion-collapse collapse" data-bs-parent="#accordionFlushExample">
            <div class="accordion-body">content 3</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>

  </body>
</html>
Via Active questions tagged javascript - Stack Overflow https://ift.tt/ye8zq9r

Comments

Popular posts from this blog

Prop `className` did not match in next js app

I have written a sample code ( Github Link here ). this is a simple next js app, but giving me error when I refresh the page. This seems to be the common problem and I tried the fix provided in the internet but does not seem to fix my issue. The error is Warning: Prop className did not match. Server: "MuiBox-root MuiBox-root-1" Client: "MuiBox-root MuiBox-root-2". Did changes for _document.js, modified _app.js as mentioned in official website and solutions in stackoverflow. but nothing seems to work. Could someone take a look and help me whats wrong with the code? Via Active questions tagged javascript - Stack Overflow https://ift.tt/2FdjaAW

How to show number of registered users in Laravel based on usertype?

i'm trying to display data from the database in the admin dashboard i used this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count(); echo $users; ?> and i have successfully get the correct data from the database but what if i want to display a specific data for example in this user table there is "usertype" that specify if the user is normal user or admin i want to user the same code above but to display a specific usertype i tried this: <?php use Illuminate\Support\Facades\DB; $users = DB::table('users')->count()->WHERE usertype =admin; echo $users; ?> but it didn't work, what am i doing wrong? source https://stackoverflow.com/questions/68199726/how-to-show-number-of-registered-users-in-laravel-based-on-usertype

Why is my reports service not connecting?

I am trying to pull some data from a Postgres database using Node.js and node-postures but I can't figure out why my service isn't connecting. my routes/index.js file: const express = require('express'); const router = express.Router(); const ordersCountController = require('../controllers/ordersCountController'); const ordersController = require('../controllers/ordersController'); const weeklyReportsController = require('../controllers/weeklyReportsController'); router.get('/orders_count', ordersCountController); router.get('/orders', ordersController); router.get('/weekly_reports', weeklyReportsController); module.exports = router; My controllers/weeklyReportsController.js file: const weeklyReportsService = require('../services/weeklyReportsService'); const weeklyReportsController = async (req, res) => { try { const data = await weeklyReportsService; res.json({data}) console