skip to Main Content

I have multiple buttons. When clicking on one of the buttons, I want to add a small checkmark after the button text and remove it after 1 second with a timeout.

This works fine as long as I don’t click 2 or more buttons within the 1 second time frame.

To see the issue, just click 2 or 3 buttons within 1 second. The checkmark will only be removed on the last clicked button.

How can I solve this issue? I would think the timeout could be bound to the clicked element but I don’t know how to achieve that.

// Get all .copy-clip buttons
let copyEls = document.querySelectorAll('.copy-clip');
let timeoutHandle;

// For each button
copyEls.forEach(el => {

  // Register click event listener
  el.addEventListener('click', (e) => {

    // Reset the inner HTML to avoid multiple copy checkmarks
    e.target.innerHTML = 'Copy';

    // Reset timeout
    window.clearTimeout(timeoutHandle);

    // Add copy checkmark in button inner HTML
    e.target.innerHTML += '<span class="copy-check"> ✓</span>';

    // Remove copy checkmark after 1 sec
    timeoutHandle = setTimeout(function() {
      e.target.innerHTML = 'Copy'
    }, 1000);
  })
})
button {
  margin-bottom: 5px;
}
<button class="copy-clip">Copy</button><br>
<button class="copy-clip">Copy</button><br>
<button class="copy-clip">Copy</button><br>

3

Answers


  1. Because there’s only one timeoutHandle. So as soon as a second value is assigned to it, the previous value is lost. Instead, create a new one in each iteration of the loop. For example:

    // Copy to clipboard
    let copyEls = document.querySelectorAll('.copy-clip');
    
    // For each copy button
    copyEls.forEach(el => {
    
      let timeoutHandle; // <-- here
    
      // Register click event listener
      el.addEventListener('click', (e) => {
    
        // Reset the inner HTML to avoid multiple copy checkmarks
        e.target.innerHTML = 'Copy';
    
        // Reset timeout
        window.clearTimeout(timeoutHandle);
    
        // Add copy checkmark in button inner HTML
        e.target.innerHTML += '<span class="copy-check"> &#x2713;</span>';
    
        // Remove copy checkmark after 1 sec
        timeoutHandle = setTimeout(function() {
          e.target.innerHTML = 'Copy'
        }, 1000);
      })
    })
    button {
      margin-bottom: 5px;
    }
    <button class="copy-clip">Copy</button><br>
    <button class="copy-clip">Copy</button><br>
    <button class="copy-clip">Copy</button><br>
    Login or Signup to reply.
  2. Move let timeoutHandle; into the loop

    // Copy to clipboard
    let copyEls = document.querySelectorAll('.copy-clip');
    
    
    // For each copy button
    copyEls.forEach(el => {
    
      let timeoutHandle;
    
      // Register click event listener
      el.addEventListener('click', (e) => {
    
        // Reset the inner HTML to avoid multiple copy checkmarks
        e.target.innerHTML = 'Copy';
    
        // Reset timeout
        window.clearTimeout(timeoutHandle);
    
        // Add copy checkmark in button inner HTML
        e.target.innerHTML += '<span class="copy-check"> &#x2713;</span>';
    
        // Remove copy checkmark after 1 sec
        timeoutHandle = setTimeout(function() {
          e.target.innerHTML = 'Copy'
        }, 1000);
      })
    })
    button {
      margin-bottom: 5px;
    }
    <button class="copy-clip">Copy</button><br>
    <button class="copy-clip">Copy</button><br>
    <button class="copy-clip">Copy</button><br>
    Login or Signup to reply.
  3. Simple, delegated and does not add/remove DOM elements

    Storing the timeout handle in the button

    document.getElementById('buttonContainer').addEventListener('click', (e) => {
      const tgt = e.target.closest("button.copy-clip");
      if (!tgt) return;
      clearTimeout(tgt.dataset.tm);
      const check = tgt.querySelector('.copy-check');
      check.hidden = false;
      tgt.dataset.tm = setTimeout(() => check.hidden = true, 1000);
    })
    button {
      margin-bottom: 5px;
    }
    <div id="buttonContainer">
      <button class="copy-clip">Copy <span class="copy-check" hidden> &#x2713;</span></button><br>
      <button class="copy-clip">Copy <span class="copy-check" hidden> &#x2713;</span></button><br>
      <button class="copy-clip">Copy <span class="copy-check" hidden> &#x2713;</span></button><br>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search