Uczymy się korzystać z nowego clipboard API dostępnego teraz w obiekcie navigator. Plus pewne sztuczki z setTimeout i prostym efektem pokazującym, że element został skopiowany do schowka.

Oto nasz markup HTMLowy:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <p id="lorem">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint molestias quis ut perspiciatis ullam, voluptate modi possimus, non repellendus ratione esse eius! Recusandae rerum, exercitationem vero ratione vitae doloremque vel!</p>
    <button id="copyBtn">Kopiuj</button>
    <script>
        window.addEventListener('DOMContentLoaded', function(){
        
        });
    </script>
</body>
</html>

Mamy tutaj paragraf z „lorem ipsum” oraz przycisk kopiuj i skrypt. Napiszemy teraz kod wykorzystujący nowy clipboard API z obiektu navigator do przekopiowania tekstu z paragrafu do schowka. Na początek – łapiemy nasze elementy:

window.addEventListener('DOMContentLoaded', function(){
    let btn = document.querySelector('#copyBtn');
    let para = document.querySelector('#lorem');

Zaczniemy trochę od końca, bowiem napiszemy sobie funkcję buttonEffect, która zamienia tekst przycisku na „Copied!” (na 1.5 sekundy):

function buttonEffect() {
  btn.textContent = 'Copied!';
  setTimeout(() => {
    btn.textContent = "Kopiuj";
    }, 1500);
}
buttonEffect();

Po wywołaniu i sprawdzeniu, że działa warto sobie wywołanie buttonEffect zakomentować albo usunąć. Teraz piszemy event-listener dla naszego przycisku, wykorzystujący navigator clipboard API:

btn.addEventListener('click', function(){
   navigator.clipboard.writeText(lorem.textContent)
   .then(() => {
     buttonEffect()
      })
   .catch(err => {
     console.error('Failed to copy text: ', err);
     });
 });

Działa jak należy. Możemy teraz jeszcze usprawnić sobie buttonEffect – na przykład zablokować możliwość wciskania przycisku przez 1.5 sekundy:

function buttonEffect() {
   btn.textContent = 'Copied!';
   btn.setAttribute('disabled', true);
   setTimeout(() => {
     btn.textContent = "Kopiuj";
     btn.removeAttribute('disabled');
     }, 1500);
}

Nie jest to idealne rozwiązanie – ale działa. Mi chodziło o coś, co sprawiłoby, że po pierwszym wciśnięciu, gdy jeszcze mamy tekst 'Copied1′ zamiast 'Kopiuj’, przez te 1.5 sekundy użytkownik nie może klikać dalej kolejkując kolejne timeouty. Można to osiągnąć na inny sposób:

let timeoutID = null;
function buttonEffect() {
   btn.textContent = 'Copied!';
   if(timeoutID)
      clearTimeout(timeoutID);
   timeoutID = setTimeout(() => {
          btn.textContent = "Kopiuj";
          }, 1500);
}

Jeżeli czujemy się bardzo pewnie, możemy spróbować napisać jakąś animację korzystając z Web Animations API. Ja na szybko coś takiego stworzyłem:

function buttonEffect() {
   btn.textContent = 'Copied!';
   if(timeoutID)
     clearTimeout(timeoutID);
   timeoutID = setTimeout(() => {
      btn.textContent = "Kopiuj";
    }, 800);
    btn.animate([
     {transform: "scale(1)"},
     {transform: "scale(1.4)"},
     {transform: "scale(1)"},
    ], {duration: 200, iterations: 1});
 }

Zaczynamy z wielkości normalnej, powiększamy guzik do 1.4 jego oryginalnej wielkości i wracamy, wszystko trwa 200ms, powtórzone 1 raz. O Web Animations API też coś napiszemy w przyszłości, na razie niech nam wystarczy ten przykład.