Search code examples
javascriptcssdelay

Don't Override setTimeout


I am trying to create a notification system that gives custom notifications. Here is my function:

var notificationCount = 0;

document.querySelector("body").innerHTML += '<div class="notification-holder"></div>';

function notification(content){
  notificationCount+=1;
  document.querySelector(".notification-holder").innerHTML += `
  <div class="notification" id="notification-${notificationCount}">
      <p>${content}</p>
  </div>
  `;
  var msg = document.querySelector(`#notification-${notificationCount}`);
  msg.style.animation = "notificationAnimate 0.2s forwards"
  msg.addEventListener("animationend", () => {
    msg.style.visibility = "visible";
    msg.style.animation = "";
    setTimeout(() => {
      msg.style.animation="notificationAnimate 0.2s reverse"
      msg.addEventListener("animationend", () => {
        msg.remove()
      })
    }, 1000)
  })
};
:root{
    --black: #151515;
    --white: #EDEDEE;
}

.notification-holder{
    position: absolute;
    bottom: 0px;
    right: 10px;
    padding-bottom: 10px;
    overflow: hidden;
}
.notification{
    width: 250px;
    padding: 10px;
    text-align: center;
    background-color: var(--black);
    color: var(--white);
    font-size: 14px;
    font-family: "Poppins", sans-serif;
    border-radius: 10px;
}
.notification:not(:first-of-type){
    margin-top: 10px;
}

@keyframes notificationAnimate{
    0%{
        opacity: 0;
        max-height: 1px;
        transform: translateY(100px);
        scale: 0;
    }
    100%{
        opacity: 1;
        max-height: fit-content;
        transform: translateY(0px);
        /* visibility: visible; */
        scale: 1;
    }
}
<html>
    <head>
    </head>
    <body>
        <button onclick="notification('Dark theme has been enabled!')">Dark theme </button>
    </body>
</html>

The code works perfectly fine for 1 notification at a time. However, when there are multiple notifications at a time, only the latest one goes through the reverse animation.

Regenerate Problem:

  • Click on the button twice
  • You will see only the latest notification goes back down. Earlier ones just stick there.

Expectation: I want all of them to go back down after 1s of when they were shown.


Solution

  • As noted in my earlier comment, I think the problem comes with you using notificationCount in the ID/selector. That changes over time.

    My solution removes the need for that, as well as all the document re-querying you were doing over time. Instead of tracking IDs and querying the DOM repeatedly, I create elements and store references to them, which I later act upon as needed.

    My changes are in the JS, the rest of this snippet is from your post:

    var notificationContainer,
        notification;
    
    notificationContainer = document.createElement('div');
    notificationContainer.className = 'notification-holder';
    document.body.appendChild(notificationContainer);
    
    notification = function (content) {
        var msg = document.createElement('div');
        msg.className = 'notification';
        msg.innerHTML = `<p>${content}</p>`;    
        msg.style.animation = 'notificationAnimate 0.2s forwards';
        msg.addEventListener('animationend', function () {
            msg.style.visibility = 'visible';
            msg.style.animation = '';
    
            setTimeout(function () {
                msg.style.animation='notificationAnimate 0.2s reverse';
                msg.addEventListener('animationend', function () {
                    msg.remove();
                });
            }, 1000);
        });
    
        notificationContainer.appendChild(msg);    
    };
    :root{
        --black: #151515;
        --white: #EDEDEE;
    }
    
    .notification-holder{
        position: absolute;
        bottom: 0px;
        right: 10px;
        padding-bottom: 10px;
        overflow: hidden;
    }
    .notification{
        width: 250px;
        padding: 10px;
        text-align: center;
        background-color: var(--black);
        color: var(--white);
        font-size: 14px;
        font-family: "Poppins", sans-serif;
        border-radius: 10px;
    }
    .notification:not(:first-of-type){
        margin-top: 10px;
    }
    
    @keyframes notificationAnimate{
        0%{
            opacity: 0;
            max-height: 1px;
            transform: translateY(100px);
            scale: 0;
        }
        100%{
            opacity: 1;
            max-height: fit-content;
            transform: translateY(0px);
            /* visibility: visible; */
            scale: 1;
        }
    }
    <html>
        <head>
        </head>
        <body>
            <button onclick="notification('Dark theme has been enabled!')">Dark theme </button>
        </body>
    </html>