htmlcsseventspseudo-element

Animation cuts when i stop pressing :active css


i've been designing a button, then you press, then appears an ::after with content "copied!"
Ok, while ::after appears there is an animation, that animation triggers with :active property, but when i stop pressing the animation ends brutally, interrupts. doesnt end properly
how can i stop pressing and the animation ends properly?
here my code!

* {
            font-family: monospace;
        }
        table{
            table-layout: fixed;
            width: 100%;
            border-collapse: collapse;
            border: 2px dashed black;
        }
        td, th{
            padding: 5px;
            border: 2px dashed black;
            text-align: center;
        }
        tr td:nth-child(4) span {
            cursor: pointer;
            background: #95c1e3;
            color: white;
            padding: 12px;
            border-radius: 50px;
            font-weight: bolder;
            font-size: 17px;
        }
        tr td:nth-child(4) span:active::after {
            display: block;
            position: absolute;
            width: 100px;
            height: max-content;
            padding: 10px 0px 10px 0px;
            background: black;
            border-radius: 50px;
            animation: copied 1s;
            content: "copiado!";
            opacity: 0;
        }
        @keyframes copied {
            0%{
                margin-top: 0;
                opacity: 1;
            }
            100%{
                margin-top: 15px;
                opacity: 0;
            }
        }
<table>
   <thead>
      <tr>
         <th>Server</th>
         <th>Nombre</th>
         <th>Jugadores</th>
         <th>IP</th>
      </tr>
   </thead>
   <tbody id="tabla">
      <tr>
         <td></td>
         <td>Sin nombre</td>
         <td>1399 / 5000</td>
         <td><span onclick="onclicker()">hub.mc-complex.com</span></td>
      </tr>
      <tr>
         <td></td>
         <td>      ! SuperCraft Network [1.8-1.21] ❤ !
            Amigos en: discord.supercraft.es
         </td>
         <td>333 / 2000</td>
         <td><span onclick="onclicker()">supercraft.es</span></td>
      </tr>
      <tr>
         <td></td>
         <td>   1.8-1.21
            NUEVO &gt; 15 REGALOS DE NAVIDAD
         </td>
         <td>189 / 1000</td>
         <td><span onclick="onclicker()">play.zonecraft.es</span></td>
      </tr>
      <tr>
         <td></td>
         <td>      ! UniversoCraft Network [1.8-1.21] ❤ !
            Chatea en: discord.universocraft.com
         </td>
         <td>5427 / 20000</td>
         <td><span onclick="onclicker()">mc.universocraft.com</span></td>
      </tr>
   </tbody>
</table>


Solution

  • As @A Haworth mentioned, you could consider using JavaScript to play the animation.

    Here, we use a copy class to show the ::after element and then remove the class one frame later. This then plays the transition, which is independent of wether the :active pseudo class matches. This means there is no abrupt end.

    document.body.addEventListener('click', ({ target }) => {
      if (target.matches('span[onclick="onclicker()"]')) {
        // Add the copy class to show the `::after`.
        target.classList.add('copy');
        // Go through two animation frames to ensure we are on the next frame.
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
              // Remove the copy class to hide the `::after`.
              target.classList.remove('copy');
            });
        });
      }
    });
    
    window.onclicker = () => {};
    * {
      font-family: monospace;
    }
    table {
      table-layout: fixed;
      width: 100%;
      border-collapse: collapse;
      border: 2px dashed black;
    }
    td,
    th {
      padding: 5px;
      border: 2px dashed black;
      text-align: center;
    }
    tr td:nth-child(4) span {
      cursor: pointer;
      background: #95c1e3;
      color: white;
      padding: 12px;
      border-radius: 50px;
      font-weight: bolder;
      font-size: 17px;
    }
    tr td:nth-child(4) span::after {
      display: block;
      position: absolute;
      width: 100px;
      height: max-content;
      padding: 10px 0px 10px 0px;
      background: black;
      border-radius: 50px;
      content: "copiado!";
      opacity: 0;
      translate: 0 15px;
      transition: translate 1s, opacity 1s;
    }
    tr td:nth-child(4) span.copy::after {
      opacity: 1;
      translate: 0 0;
      transition: 0s;
    }
    <table>
      <thead>
        <tr>
          <th>Server</th>
          <th>Nombre</th>
          <th>Jugadores</th>
          <th>IP</th>
        </tr>
      </thead>
      <tbody id="tabla">
        <tr>
          <td></td>
          <td>Sin nombre</td>
          <td>1399 / 5000</td>
          <td><span onclick="onclicker()">hub.mc-complex.com</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! SuperCraft Network [1.8-1.21] ❤ ! Amigos en: discord.supercraft.es</td>
          <td>333 / 2000</td>
          <td><span onclick="onclicker()">supercraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>1.8-1.21 NUEVO &gt; 15 REGALOS DE NAVIDAD</td>
          <td>189 / 1000</td>
          <td><span onclick="onclicker()">play.zonecraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! UniversoCraft Network [1.8-1.21] ❤ ! Chatea en: discord.universocraft.com</td>
          <td>5427 / 20000</td>
          <td><span onclick="onclicker()">mc.universocraft.com</span></td>
        </tr>
      </tbody>
    </table>

    An alternative JavaScript solution could be to use the Web Animations API:

    document.body.addEventListener('click', ({ target }) => {
      if (target.matches('span[onclick="onclicker()"]')) {
        const keyframes = [
          {
            opacity: 1,
            translate: '0 0',
          },
          {
            opacity: 0,
            translate: '0 15px',
          },
        ];
        const options = {
          duration: 1000,
          timing: 'cubic-bezier(0.32, 0, 0.67, 0)',
          pseudoElement: '::after',
        };
        target.animate(keyframes, options);
      }
    });
    
    window.onclicker = () => {};
    * {
      font-family: monospace;
    }
    table {
      table-layout: fixed;
      width: 100%;
      border-collapse: collapse;
      border: 2px dashed black;
    }
    td,
    th {
      padding: 5px;
      border: 2px dashed black;
      text-align: center;
    }
    tr td:nth-child(4) span {
      cursor: pointer;
      background: #95c1e3;
      color: white;
      padding: 12px;
      border-radius: 50px;
      font-weight: bolder;
      font-size: 17px;
    }
    tr td:nth-child(4) span::after {
      display: block;
      position: absolute;
      width: 100px;
      height: max-content;
      padding: 10px 0px 10px 0px;
      background: black;
      border-radius: 50px;
      content: "copiado!";
      opacity: 0;
      translate: 0 15px;
    }
    <table>
      <thead>
        <tr>
          <th>Server</th>
          <th>Nombre</th>
          <th>Jugadores</th>
          <th>IP</th>
        </tr>
      </thead>
      <tbody id="tabla">
        <tr>
          <td></td>
          <td>Sin nombre</td>
          <td>1399 / 5000</td>
          <td><span onclick="onclicker()">hub.mc-complex.com</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! SuperCraft Network [1.8-1.21] ❤ ! Amigos en: discord.supercraft.es</td>
          <td>333 / 2000</td>
          <td><span onclick="onclicker()">supercraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>1.8-1.21 NUEVO &gt; 15 REGALOS DE NAVIDAD</td>
          <td>189 / 1000</td>
          <td><span onclick="onclicker()">play.zonecraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! UniversoCraft Network [1.8-1.21] ❤ ! Chatea en: discord.universocraft.com</td>
          <td>5427 / 20000</td>
          <td><span onclick="onclicker()">mc.universocraft.com</span></td>
        </tr>
      </tbody>
    </table>

    For a CSS-only alternative, you could have the element be shown while :active matches and then have it fade out when it longer does:

    document.body.addEventListener('click', ({ target }) => {
      if (target.matches('span[onclick="onclicker()"]')) {
        // Add the copy class to show the `::after`.
        target.classList.add('copy');
        // Go through two animation frames to ensure we are on the next frame.
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
              // Remove the copy class to hide the `::after`.
              target.classList.remove('copy');
            });
        });
      }
    });
    
    window.onclicker = () => {};
    * {
      font-family: monospace;
    }
    table {
      table-layout: fixed;
      width: 100%;
      border-collapse: collapse;
      border: 2px dashed black;
    }
    td,
    th {
      padding: 5px;
      border: 2px dashed black;
      text-align: center;
    }
    tr td:nth-child(4) span {
      cursor: pointer;
      background: #95c1e3;
      color: white;
      padding: 12px;
      border-radius: 50px;
      font-weight: bolder;
      font-size: 17px;
    }
    tr td:nth-child(4) span::after {
      display: block;
      position: absolute;
      width: 100px;
      height: max-content;
      padding: 10px 0px 10px 0px;
      background: black;
      border-radius: 50px;
      content: "copiado!";
      opacity: 0;
      translate: 0 15px;
      transition: translate 1s, opacity 1s;
    }
    tr td:nth-child(4) span:active::after {
      opacity: 1;
      translate: 0 0;
      transition: 0s;
    }
    <table>
      <thead>
        <tr>
          <th>Server</th>
          <th>Nombre</th>
          <th>Jugadores</th>
          <th>IP</th>
        </tr>
      </thead>
      <tbody id="tabla">
        <tr>
          <td></td>
          <td>Sin nombre</td>
          <td>1399 / 5000</td>
          <td><span onclick="onclicker()">hub.mc-complex.com</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! SuperCraft Network [1.8-1.21] ❤ ! Amigos en: discord.supercraft.es</td>
          <td>333 / 2000</td>
          <td><span onclick="onclicker()">supercraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>1.8-1.21 NUEVO &gt; 15 REGALOS DE NAVIDAD</td>
          <td>189 / 1000</td>
          <td><span onclick="onclicker()">play.zonecraft.es</span></td>
        </tr>
        <tr>
          <td></td>
          <td>! UniversoCraft Network [1.8-1.21] ❤ ! Chatea en: discord.universocraft.com</td>
          <td>5427 / 20000</td>
          <td><span onclick="onclicker()">mc.universocraft.com</span></td>
        </tr>
      </tbody>
    </table>