0

I have problem with Boolean flag.

There is no problem if there's just one button, but with multiple buttons it's problematic. After first click the value of the flag is false. But to change the second button's arrow the flag value true value is needed... and so on...

Please click every button one after another to see what I mean.

How can I solve this issue?

  var btn = document.querySelectorAll('.cdi-link');
  var flag = true;

  for (var i = 0; i < btn.length; i++) {
    btn[i].addEventListener('click', function(){
      var button = this;
      var arrow = button.lastElementChild.lastElementChild;

      if (flag) {
        flag = false;
        arrow.innerHTML = '&#9660;';
        console.log(flag);
      } else {
        flag = true;
        arrow.innerHTML = '&#9654;';
        console.log(flag);
      }
    });
  }
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

3
  • So you want every button to have its own flag? Commented Feb 13, 2018 at 14:25
  • You need more than one flag, one per button. Commented Feb 13, 2018 at 14:26
  • Creating an IIFE in a loop should be seen as a last resort solution. For some reason, it's the first thing people reach for, but it's often very unnecessary. Commented Feb 13, 2018 at 15:12

5 Answers 5

3

You could use a closure over the flag by using a function which creates an own scope for flag.

var btn = document.querySelectorAll('.cdi-link');

for (var i = 0; i < btn.length; i++) {
    btn[i].addEventListener('click', function (flag) {
        return function () {
            var button = this;
            var arrow = button.lastElementChild.lastElementChild;

            if (flag) {
                flag = false;
                arrow.innerHTML = '&#9660;';
                console.log(flag);
            } else {
                flag = true;
                arrow.innerHTML = '&#9654;';
                console.log(flag);
            }
        };
    }(true)); // start value for flag
}
<button type="button" class="cdi-link"><div><span>click to change the arrow</span> <span class="arrow">&#9654;</span></div></button>
<button type="button" class="cdi-link"><div><span>click to change the arrow</span> <span class="arrow">&#9654;</span></div></button>
<button type="button" class="cdi-link"><div><span>click to change the arrow</span> <span class="arrow">&#9654;</span></div></button>

Sign up to request clarification or add additional context in comments.

Comments

1

You need to either associate state with each button, or in this case you could simply toggle a class.

var btn = document.querySelectorAll('.cdi-link');
  for (var i = 0; i < btn.length; i++) {
    btn[i].addEventListener('click', function() {
      this.classList.toggle('cdi-link--open');
    });
  }
.cdi-link .arrow::after {
  content: "▲";
 }
 
 .cdi-link--open .arrow::after {
  content: "▼";
 }
<button type="button" class="cdi-link">
    <div>
      <span>click to change the arrow</span> <span class="arrow"></span>
    </div>
  </button>

  <button type="button" class="cdi-link">
    <div>
      <span>click to change the arrow</span> <span class="arrow"></span>
    </div>
  </button>

  <button type="button" class="cdi-link">
    <div>
      <span>click to change the arrow</span> <span class="arrow"></span>
    </div>
  </button>

Comments

1

One solution is to store an individual flag inside each button using this.flag :

var btn = document.querySelectorAll('.cdi-link');


  for (var i = 0; i < btn.length; i++) {
    btn[i].addEventListener('click', function(){
      var button = this
      var arrow = button.lastElementChild.lastElementChild;
      
      if (this.flag) {
        this.flag = false;
        arrow.innerHTML = '&#9654;';
      } else {
        this.flag = true;
        arrow.innerHTML = '&#9660;';
      }
      console.log("Flag = " + this.flag);
    });
  }
<button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

Comments

1

Keep it simple and put a property directly on the element (because you're just storing primitive data anyway). Just be sure to give it a name that won't likely conlict with any other code.

  var btn = document.querySelectorAll('.cdi-link');

  for (var i = 0; i < btn.length; i++) {
    btn[i].__my_flag__ = true;
    btn[i].addEventListener('click', function(){
      var button = this;
      var arrow = button.lastElementChild.lastElementChild;

      if (button.__my_flag__) {
        button.__my_flag__ = false;
        arrow.innerHTML = '&#9660;';
        console.log(button.__my_flag__);
      } else {
        button.__my_flag__ = true;
        arrow.innerHTML = '&#9654;';
        console.log(button.__my_flag__);
      }
    });
  }
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

This is memory safe, even in old browsers, and uses less memory overhead than closure solutions, which actually do sometimes have memory leak problems in old browsers.

Comments

1

You can use IIFE (Immediately Invoked Function Expression). Declare flag inside IIFE to create self executing block in each iteration of for loop. Try the following:

var btn = document.querySelectorAll('.cdi-link');

for (var i = 0; i < btn.length; i++) {
  (function(){
    var flag = true;
    btn[i].addEventListener('click', function(){
      var button = this;
      var arrow = button.lastElementChild.lastElementChild;

      if (flag) {
        flag = false;
        arrow.innerHTML = '&#9660;';
        console.log(flag);
      } else {
        flag = true;
        arrow.innerHTML = '&#9654;';
        console.log(flag);
      }
    });
  })();
}
<button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>
          
    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

    <button type="button" class="cdi-link">
      <div>
        <span>click to change the arrow</span> <span class="arrow">&#9654;</span>
      </div>
    </button>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.