DEV Community

Jesse Wei
Jesse Wei

Posted on

Implement Click-And-Copy With Clipboard API

Click-and-copy is everywhere these days. By simplifying a multi-step action to a single-step one, this functionality makes web users happy so as a UI/UX designer, you don't want to miss out on it.

At the core of the implementation is the Clipboard API. It may sound daunting at first, but the logic is actually quite straightforward. I'll help you get familiar with it by building a real world example together in this tutorial.


Table of Contents


What We Will Build

Our example will look and work like this when finished:

Click-and-copy example

Click-and-copy example

When you click a coupon code, it will be selected and highlighted, and a tooltip with the copied coupon code will appear. You can then paste the content to the coupon code input to apply it, which mimics how we work with coupon code in the real world.

For brevity, I will only show the code that's relevant to the step in the description below. You can find the full code of the example here.


HTML & CSS

Let's start by creating a index.html and style.css file at our project root.

Give index.html the following content (full code can be found here)

<div class="container">
  <div class="coupons">
    <div class="coupon">
      <small class="coupon-label">Coupon Code: </small>
      <input type="text" name="coupon-code-1" id="coupon-code-1" size="10" value="ABC12345" readonly>
      <small class="tooltip"><span class="copied-code"></span> Copied</small>
    </div>
    <div class="coupon">
      <small class="coupon-label">Coupon Code: </small>
      <input type="text" name="coupon-code-2" id="coupon-code-2" size="10" value="XYZ098765" readonly>
      <small class="tooltip"><span class="copied-code"></span> Copied</small>
    </div>
  </div>
  <form autocomplete="off">
    <label for="code-input">Your Coupon Code: </label>
    <div>
      <input type="text" name="code-input" id="code-input">
      <button type="submit">Apply</button>
    </div>
  </form>
</div>
Enter fullscreen mode Exit fullscreen mode

Then give style.css the following content (full code can be found here)

body {
  height: 50vh;
  background-color: beige;
  display: flex;
  justify-content: center;
  align-items: center;
}
.container {
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.coupons {
  margin-bottom: 20px;
}
@media (min-width: 768px) {
  .coupons {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 2%;
  }
}
.coupon {
  background-color: white;
  border: 2px solid black;
  display: flex;
  justify-content: space-between;
  align-items: center;
  position: relative;
  padding: 10px;
  margin-bottom: 20px;
}
.coupon:last-child {
  margin-bottom: 0;
}
@media (min-width: 768px) {
  .coupon {
    margin-bottom: 0;
  }
}
.coupon-label {
  white-space: nowrap;
  margin-right: 5px;
}
input[name^='coupon-code-'] {
  border: none;
  outline: none;
  font-size: large;
  font-weight: bold;
  text-align: center;
}
.tooltip {
  background-color:darkslategray;
  color: white;
  padding: 5px 10px;
  border-radius: 5px;
  white-space: nowrap;
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translate(-50%, -6px);
  display: none;
}
.tooltip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  border-top: 5px solid darkslategray;
  border-bottom: 5px solid transparent;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
form {
  width: 100%;
}
form div {
  width: 100%;
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 2%;
}
label[for='code-input'] {
  display: block;
  margin-bottom: 10px;
}
input[name='code-input'] {
  grid-column: span 3 / span 3;
  padding: 10px;
  border: 1px solid black;
}
button[type='submit'] {
  grid-column: span 1 / span 1;
  border: 2px solid black;
  background-color: transparent;
  padding: 10px 20px;
}
Enter fullscreen mode Exit fullscreen mode

Clipboard API

An Overview

Before implementing the API, let's take a quick look at what we can do with it.

This API allows us to access the system clipboard through the following methods:

  • read(). Get the data (not limited to text) from the clipboard.
  • readText(). Get the text data from the clipboard.
  • write(). Write data (not limited to text) to the clipboard.
  • writeText(). Write text data to the clipboard. Now let's implement the Clipboard API in Javascript to give our example the expected behavior.

All the methods return a Promise. For the read* methods, the promise is resolved with the data of the clipboard and for the write* methods, the promise is resolved when the data is copied to the clipboard.

Note that,

  • We access the Clipboard object through the navigator.clipboard global.
  • The support of the API varies across browsers. Check out browser compatibility to see if your browser supports it.
  • Permission is required for the API to access the system clipboard, so an alert may appear that asks for your permission when calling the API for the first time (if that's the case, click allow).

Elements and Event Listeners

Now let's get started coding! First, create a script.js file at the project root. Then, let's select and store the elements we'll use in variables and prepare our event listeners:

const codeEls = document.querySelectorAll('input[name^="coupon-code-"]')
const tooltips = document.querySelectorAll('.tooltip')
const form = document.querySelector('form')
const codeInput = document.querySelector('input[name=code-input]')

/**
 * todo: define handleCopy and handleSubmit methods
 */

for (const codeEl of codeEls) {
  codeEl.addEventListener('click', handleCopy(codeEl))
}

form.addEventListener('submit', handleSubmit)
Enter fullscreen mode Exit fullscreen mode

Click-and-Copy Handler

The handleCopy() method contains the core business logic for the click-and-copy action. In our example, we only deal with text data, so we use readText() and writeText() here.

let timeout

const handleCopy = el => {
  return () => {
    /**
     * 1. copy coupon code to clipboard
     */
    navigator.clipboard.writeText(el.value)
      .then(() => {
        /**
         * 2. select and highlight clicked coupon code
         */
        el.setSelectionRange(0, -1)
        /**
         * 3. hide all currently displayed tooltips
         *    if there is any
         */
        tooltips.forEach(tooltip => tooltip.style.display = 'none')
        /**
         * 4. select the tooltip for the clicked coupon
         */
        const tooltip = el.nextElementSibling
        /**
         * 5. insert the copied code to the tooltip
         *    before displaying the tooltip
         */
        const copiedCodeEl = tooltip.querySelector('.copied-code')
        navigator.clipboard.readText()
          .then(code => copiedCodeEl.textContent = code)
        tooltip.style.display = 'block'
        /**
         * 6. clear any existing timeout
         *    before assigning a new one to the variable
         */
        if (typeof timeout === 'number') {
          clearTimeout(timeout)
        }
        /**
         * 7. display the tooltip for 2 seconds
         */
        timeout = setTimeout(() => {
          tooltip.style.display = 'none'
          copiedCodeEl.textContent = ''
        }, 2000)
      })
  }
}
Enter fullscreen mode Exit fullscreen mode

Form Submission Handler

In handleSubmit, we'll just get the input value of the text field and display it in an alert:

const handleSubmit = (event) => {
  event.preventDefault()
  const formData = new FormData(form)
  const code = formData.get('code-input')
  /**
   * clearing the text field after form submission
   * makes great UX
   */
  formData.set('code-input', '')
  codeInput.value = ''
  alert(`Coupon Code ${code} has been applied successfully.`)
}
Enter fullscreen mode Exit fullscreen mode

And that's it. Make sure to get your hands dirty and write the code line by line to really understand the concept.

The full Javascript code can be found here.

And the final and live example can be found here.


Conclusion

In this tutoria, we learned how to implement a click-and-copy component by building an example that can be used out of the box in your next project.

We looked at the Clipboard API which is the core to our implementation. The Javascript code looks somewhat lengthy but the idea is not that complex, especially when we break the steps down.

Top comments (0)