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

ValueError: X has 10 features, but LinearRegression is expecting 1 features as input

So, I am trying to predict the model but its throwing error like it has 10 features but it expacts only 1. So I am confused can anyone help me with it? more importantly its not working for me when my friend runs it. It works perfectly fine dose anyone know the reason about it? cv = KFold(n_splits = 10) all_loss = [] for i in range(9): # 1st for loop over polynomial orders poly_order = i X_train = make_polynomial(x, poly_order) loss_at_order = [] # initiate a set to collect loss for CV for train_index, test_index in cv.split(X_train): print('TRAIN:', train_index, 'TEST:', test_index) X_train_cv, X_test_cv = X_train[train_index], X_test[test_index] t_train_cv, t_test_cv = t[train_index], t[test_index] reg.fit(X_train_cv, t_train_cv) loss_at_order.append(np.mean((t_test_cv - reg.predict(X_test_cv))**2)) # collect loss at fold all_loss.append(np.mean(loss_at_order)) # collect loss at order plt.plot(np.log(al...

Sorting large arrays of big numeric stings

I was solving bigSorting() problem from hackerrank: Consider an array of numeric strings where each string is a positive number with anywhere from to digits. Sort the array's elements in non-decreasing, or ascending order of their integer values and return the sorted array. I know it works as follows: def bigSorting(unsorted): return sorted(unsorted, key=int) But I didnt guess this approach earlier. Initially I tried below: def bigSorting(unsorted): int_unsorted = [int(i) for i in unsorted] int_sorted = sorted(int_unsorted) return [str(i) for i in int_sorted] However, for some of the test cases, it was showing time limit exceeded. Why is it so? PS: I dont know exactly what those test cases were as hacker rank does not reveal all test cases. source https://stackoverflow.com/questions/73007397/sorting-large-arrays-of-big-numeric-stings

How to load Javascript with imported modules?

I am trying to import modules from tensorflowjs, and below is my code. test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title </head> <body> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script> <script type="module" src="./test.js"></script> </body> </html> test.js import * as tf from "./node_modules/@tensorflow/tfjs"; import {loadGraphModel} from "./node_modules/@tensorflow/tfjs-converter"; const MODEL_URL = './model.json'; const model = await loadGraphModel(MODEL_URL); const cat = document.getElementById('cat'); model.execute(tf.browser.fromPixels(cat)); Besides, I run the server using python -m http.server in my command prompt(Windows 10), and this is the error prompt in the console log of my browser: Failed to loa...