<div class="box-center">
  <!-- Copies the textContent -->
  <button class="copiable">COPY ME</button>
  
  <!-- Copies the 'data-copy-value' -->
  <button class="copiable" data-copy-value="Copy this">COPY Dataset</button>
  
  <div class="row">
    <input value="I can also be copied" id="input" />
    
    <!-- Copies the value | textContent of the target (id) -->
    <button class="copiable" data-copy-target="input">Copy</button>
  </div>
</div>
body {
  background: #4b111b;
}

.box-center {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.row {
  display: flex;
  align-items: center;
}

button {
  background: #a3243b;
  border: none;
  padding: 24px 40px;
  color: white;
  font-weight: 500;
  font-size: 18px;
  cursor: pointer;
  border-radius: 4px;
  width: 100%;
  margin: 16px 0;
}

input {
  background: #4B111B;
  border: 2px solid #a3243b;
  padding: 24px 40px;
  color: white;
  border-radius: 4px;
  margin-right: 10px;
}

button:hover {
  background-color: #f2545c;
}

.custom-toast {
  background: #a3243b;
}
View Compiled
/*
 * A snippet for copying to cliboard.
 * - Copy from button
 * - Copy from raw text
 * - Copy from input
 *
 * Browser Support:
 *
 * - execCommand('copy')
 *   Deprecated as stated here: https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#browser_compatibility
 * - Cliboard API
 *   Support at >90%: https://caniuse.com/mdn-api_clipboard_writetext
 *
 * Got some inspiration from https://stackoverflow.com/a/30810322/5900163
 *
 * Used in the real world: 
 *   - Pass genny: https://pass-genny.vercel.app/
 */


/* Actual copying logic */

// Fallback in case Clipboard API is not supported
function fallbackCopyTextToClipboard(text) {
  // Wrap in promise to resemble the aync Clipboard API
  return new Promise((resolve, reject) => {
    const textArea = document.createElement("textarea");
    textArea.value = text;

    // Avoid scrolling to bottom
    textArea.style.top = "0";
    textArea.style.left = "0";
    textArea.style.position = "fixed";

    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    try {
      const successful = document.execCommand("copy");
      successfull ? resolve() : reject();
    } catch (err) {
      reject(err);
    }

    document.body.removeChild(textArea);
  });
}

// Attempts to copy text to cliboard, either using Clipboard Async API, 
// Or falling back to "execCommand('copy')"
function copyTextToClipboard(text) {
  const cliboardSupported = navigator.clipboard;

  const copyPromise = cliboardSupported
    ? navigator.clipboard.writeText(text)
    : fallbackCopyTextToClipboard(text);

  return copyPromise.then(() => text);
}


/* Specific case setup */
const allCopiable = document.querySelectorAll(".copiable");

allCopiable.forEach((el) => {
  el.addEventListener("click", () => {
    copyTextFromElement(el).then((text) => {
      showToast(`Copied: "${text}"`);
    }).catch((e) => {
      showToast(`ERROR: "${e}"`);
    });
  });
});

function copyTextFromElement(element) {
  const target = element.dataset["copyTarget"];
  if (target) {
    const targetEl = document.getElementById(target);
    return copyTextToClipboard(targetEl.value ?? targetEl.textContent);
  }

  const text = element.dataset["copyValue"] ?? element.textContent;
  return copyTextToClipboard(text);
}


// Just for demo
function showToast(message) {
  Toastify({
    text: message,
    duration: 3000,
    gravity: "top", // `top` or `bottom`
    position: "center", // `left`, `center` or `right`
    className: "custom-toast",
    style: {
      background: "#F2545C"
    }
  }).showToast();
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.