skip to Main Content

This is a problem that I’m unable to solve. I have a solution, but the variable flag is a global variable.
The problem is under the constraint that flag can’t be global.
Essentially, we press on either ‘jQuery’ or ‘JS Mode’ buttons first and then press ‘Collapse Me’ button in order to execute a collapsible menu.
Each button is supposed to simulate the same functionality but with different code.
For now, I didn’t use jQuery, so pretend that it is.
The code is straightforward enough, but, again, I need a solution where flag is passed on to the third addEvenListener without being global.

let coll = document.getElementsByClassName("collapsible");
let jsBtn = document.getElementById("jsBtn");
let jqBtn = document.getElementById("jqueryBtn");
let flag = null;

jsBtn.addEventListener("click", () => {
  flag = "js";
});
jqBtn.addEventListener("click", () => {
  flag = "jq";
});

coll[0].addEventListener("click", nestedColl);

function nestedColl() {
  console.log(flag);
  if (flag == "js") {
    collJs(flag);
  } else if (flag == "jq") {
    pseudoJq(flag);
  }
}

function collJs(flag) {
  console.log(flag + "collJS");
  var content = coll[0].nextElementSibling;
  if (content.style.maxHeight) {
    content.style.maxHeight = null;
  } else {
    content.style.maxHeight = content.scrollHeight + "px";
  }
}

function pseudoJq(flag) {
  console.log(flag + "pseudoJq");
  var content = coll[0].nextElementSibling;
  if (content.style.maxHeight) {
    content.style.maxHeight = null;
  } else {
    content.style.maxHeight = content.scrollHeight + "px";
  }
}
.content,
.content1 {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}
<button id="jsBtn">JS Mode</button>
<button id="jqueryBtn">jQuery Mode</button>
<button class="collapsible">Collapse Me</button>

<div class="content">
  <p>
    filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
    text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
    text. filler text. filler text. filler text. filler text. filler text. filler.
  </p>
</div>

What did I try and what was I expecting? I wrapped the third addEventListener in a function and the other addEventListeners would call that function to pass on flag. But then the event doesn’t work correctly.

Code that doesn’t work but why?

jsBtn.addEventListener("click", () => {
  collFun(flag = "js");
});
jqBtn.addEventListener("click", () => {
  collFun(flag = "jq");
});

function collFun() {
  console.log(flag);
  coll[0].addEventListener("click", nestedColl);
  function nestedColl() {
    console.log(flag);
    if (flag == "js") {
      collJs(flag);
    } else if (flag == "jq") {
      pseudoJq(flag);
    }
  }
}

4

Answers


  1. You can have each mode button change which function is used as the click listener on the "Collapse me" button, using removeEventListener().

    let coll = document.getElementsByClassName("collapsible");
    let jsBtn = document.getElementById("jsBtn");
    let jqBtn = document.getElementById("jqueryBtn");
    let flag = null;
    
    jsBtn.addEventListener("click", () => {
      coll[0].removeEventListener("click", pseudoJq);
      coll[0].addEventListener("click", collJs);
    });
    jqBtn.addEventListener("click", function() {
      coll[0].removeEventListener("click", collJs);
      coll[0].addEventListener("click", pseudoJq);
    });
    
    
    function collJs() {
      console.log("collJS");
      var content = coll[0].nextElementSibling;
      if (content.style.maxHeight) {
        content.style.maxHeight = null;
      } else {
        content.style.maxHeight = content.scrollHeight + "px";
      }
    }
    
    function pseudoJq() {
      console.log("pseudoJq");
      var content = coll[0].nextElementSibling;
      if (content.style.maxHeight) {
        content.style.maxHeight = null;
      } else {
        content.style.maxHeight = content.scrollHeight + "px";
      }
    }
    .content,
    .content1 {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    <body>
      <button id="jsBtn">JS Mode</button>
      <button id="jqueryBtn">jQuery Mode</button>
      <button class="collapsible">Collapse Me</button>
    
      <div class="content">
        <p>
          filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
          text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
          text. filler text. filler text. filler text. filler text. filler text. filler.
        </p>
      </div>
    Login or Signup to reply.
  2. you can also add a property to you content element, like so:

    const
      coll_0  = document.querySelector('.collapsible')
    , jsBtn   = document.querySelector('#jsBtn')
    , jqBtn   = document.querySelector('#jqueryBtn')
    , content = coll_0.nextElementSibling
      ;
    content.flag = 'none';  // initial value...
    
    
    jsBtn.onclick =_=> { content.flag = 'js'  }
    jqBtn.onclick =_=> { content.flag = 'jq'  }
    
    //....
    
    Login or Signup to reply.
  3. Use the event that gets passed. event.target tells you which element triggered the action. You can then read properties of that element.

    document.querySelectorAll("button").forEach(function(button){
      button.addEventListener("click",function(event){
        let color = event.target.textContent;
        document.querySelector("div").style.backgroundColor=color;
      })
    })
    div {
    height: 200px;
    width: 200px;
    outline: 1px solid black;
    }
    <button>red</button>
    <button>yellow</button>
    <div></div>
    Login or Signup to reply.
  4. Instead of multiple functions, I’m delegating the click handler to the body.

    For the buttons, I add a data attribute to jQuery and Javascript. This will hold each of their representative modes.

    I also add a data attribute for flag to the collapse button. This will hold the flag for whatever was clicked on.

    Then I use one function for expanding/collapsing. I also attach an active class for future use and to hide expanded content.

    const coll = document.querySelector(".collapsible");
    
    document.body.addEventListener("click", (e) => {
      const el = e.target;
      if (el.dataset.mode) {
        coll.dataset.flag = el.dataset.mode;
      } else if (el.classList.contains("collapsible") && el.dataset.flag) {
        expandContent();
      }
    
    });
    
    function expandContent() {
      const content = document.querySelector(".content[data-mode='" + coll.dataset.flag + "']");
      const hasActive = document.querySelector(".active.content");
    
      if (hasActive) {
        hasActive.style.maxHeight = null;
        hasActive.classList.remove("active");
      }
      content.style.maxHeight = content.scrollHeight + "px";
      content.classList.add("active")
    
    }
    .content {
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    <button data-mode="js">JS Mode</button>
    <button data-mode="jQuery">jQuery Mode</button>
    <button data-flag class="collapsible">Collapse Me</button>
    
    <div data-mode="js" class="content">
      <h2>JS</h2>
      <p>
    
        filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
        text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
        text. filler text. filler text. filler text. filler text. filler text. filler.
      </p>
    </div>
    
    <div data-mode="jQuery" class="content">
      <h2>jquery</h2>
      <p>
    
        filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
        text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler text. filler
        text. filler text. filler text. filler text. filler text. filler text. filler.
      </p>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search