Home > Blockchain >  Two click functions, affecting the same div, the first work but second not - javascript
Two click functions, affecting the same div, the first work but second not - javascript

Time:09-20

I have a button which opens a popup when clicked, and in this popup there is a close button to close the popup but that close button is not working!

Here is my code:

var fromToDate = document.getElementById('from-to-date');
var openFromToDate = document.getElementById('open-from-to-date');
var closeFromToDate = document.getElementById('close-from-to-date');
openFromToDate.addEventListener('click', function() {
  fromToDate.classList.remove('dis-none');
});
closeFromToDate.addEventListener('click', function() {
  fromToDate.classList.add('dis-none');
});
.dis-none {
  display: none;
}

/* To make the button visible here */
.fa-times::after {
  content: 'X';
}
<li id="open-from-to-date">
  <span >
    From: 2022-09-16 To: 2022-09-18
  </span>
  <div  id="from-to-date">
    <div >
      <div  id="close-from-to-date">
        <i  aria-hidden="true"></i>
      </div>
    </div>
  </div>
</li>

CodePudding user response:

The problem is that when you click the close button, two listeners fire: First, the close button listener fires and adds dis-none, but then the open "button" listener also fires and removes dis-none again. This is because the close button is contained within the open "button", so any click on the close button also constitutes a click on the open "button" at the same time.

You can easily check this by adding console.log statements to the two listeners.

The solution is therefore to not let the event bubble up to any listeners on parent elements once you handled it on the close button itself, which can be done using stopPropagation:

var fromToDate = document.getElementById('from-to-date');
var openFromToDate = document.getElementById('open-from-to-date');
var closeFromToDate = document.getElementById('close-from-to-date');
openFromToDate.addEventListener('click', function() {
  fromToDate.classList.remove('dis-none');
});
closeFromToDate.addEventListener('click', function(e) {
  fromToDate.classList.add('dis-none');
  e.stopPropagation(); // <---- THIS
});
.dis-none {
  display: none;
}

/* To make the button visible here */
.fa-times::after {
  content: 'X';
}
<li id="open-from-to-date">
  <span >
    From: 2022-09-16 To: 2022-09-18
  </span>
  <div  id="from-to-date">
    <div >
      <div  id="close-from-to-date">
        <i  aria-hidden="true"></i>
      </div>
    </div>
  </div>
</li>

(Note: I also added the event argument e to the listener function so that we could call stopPropgation on it.)

CodePudding user response:

The problem is when you're clicking on the "closer" you are also clicking on the "opener" as well, say not? See the code below.

var fromToDate = document.getElementById('from-to-date');
var openFromToDate = document.getElementById('open-from-to-date');
var closeFromToDate = document.getElementById('close-from-to-date');
openFromToDate.addEventListener('click', function() {
  fromToDate.classList.remove('dis-none');
  console.log("I opened");
});
closeFromToDate.addEventListener('click', function() {
  fromToDate.classList.add('dis-none');
  console.log("I closed");
});
.dis-none {
  display: none;
}
<li id="open-from-to-date">
  <span >From: 2022-09-16 To: 2022-09-18</span>
  <div  id="from-to-date">
    <div >
      <div  id="close-from-to-date">
        <i  aria-hidden="true"></i>
        Close!
      </div>
    </div>
  </div>
</li>

Now you have two ways to correct this:

window.setTimeout

var fromToDate = document.getElementById('from-to-date');
var openFromToDate = document.getElementById('open-from-to-date');
var closeFromToDate = document.getElementById('close-from-to-date');
openFromToDate.addEventListener('click', function() {
  fromToDate.classList.remove('dis-none');
});
closeFromToDate.addEventListener('click', function() {
  setTimeout(function() {
    fromToDate.classList.add('dis-none');
  }, 2);
});
.dis-none {
  display: none;
}
<li id="open-from-to-date">
  <span >From: 2022-09-16 To: 2022-09-18</span>
  <div  id="from-to-date">
    <div >
      <div  id="close-from-to-date">
        <i  aria-hidden="true"></i> Close!
      </div>
    </div>
  </div>
</li>

In this method, the removing method runs 2ms later which means two lines of JS code later.

Capture parameter of addEventListener which usually is overseen

RECOMMENDED

See this example:

var fromToDate = document.getElementById('from-to-date');
var openFromToDate = document.getElementById('open-from-to-date');
var closeFromToDate = document.getElementById('close-from-to-date');
openFromToDate.addEventListener('click', function() {
  fromToDate.classList.remove('dis-none');
  console.log("I'm opened");
}, true); //Magic happens here
closeFromToDate.addEventListener('click', function() {
  fromToDate.classList.add('dis-none');
  console.log("I'm closed");
});
.dis-none {
  display: none;
}
<li id="open-from-to-date">
  <span >From: 2022-09-16 To: 2022-09-18</span>
  <div  id="from-to-date">
    <div >
      <div  id="close-from-to-date">
        <i  aria-hidden="true"></i> Close!
      </div>
    </div>
  </div>
</li>

In this example, the capturing method of the opening event is set to true which means, the event listening is in the capturing phase instead of the bubbling phase which is the default.

In the capturing phase, the outmost event listener runs first so the opening event is captured first and closing event, next.

For more information, see here.

  • Related