Search code examples
javascripthtmlcssurlcopy

Copy text to clipboard with JS


I am a JS beginner and I have the following problem: I want that as soon as someone clicks on the URL icon inside the accordion the respective link is copied to the clipboard. Unfortunately (always) only the first link is copied to the clipboard, even if one clicks on the other two URL icons only the first link is copied. Although in the clipboard should be link 2 (from the value field) when i click on URL icon 2 (and the same for number 3 of course). I hope I have described the problem clearly enough.

Where is the error and what do I need to change on the JS code to make it work? Thanks a lot for the help in advance!

```
<!DOCTYPE html>
<html>
<head>
  <title>My example Website</title>
<style>
body {
  font-size: 21px;
  font-family: Tahoma, Geneva, sans-serif;
  max-width: 550px;
  margin: 0 auto;
  background-color: black;
}
input {
  display: none;
}
label {
  display: block;    
  padding: 8px 22px;
  margin: 0 0 1px 0;
  cursor: pointer;
  background: #181818;
  border: 1px solid white;
  border-radius: 5px;
  color: #FFF;
  position: relative;
}
label:hover {
  background: white;
  border: 1px solid white;
  color:black;
}
label::after {
  content: '+';
  font-size: 22px;
  font-weight: bold;
  position: absolute;
  right: 10px;
  top: 2px;
}
input:checked + label::after {
  content: '-';
  right: 14px;
  top: 3px;
}
.content {
  background: #DBEECD;
  background: -webkit-linear-gradient(bottom right, #DBEECD, #EBD1CD);
  background: -moz-linear-gradient(bottom right, #DBEECD, #EBD1CD);
  background: linear-gradient(to top left, #DBEECD, #EBD1CD);
  padding: 10px 25px 10px 25px;
  border: 1px solid #A7A7A7;
  margin: 0 0 1px 0;
  border-radius: 1px;
}
input + label + .content {
  display: none;
}
input:checked + label + .content {
  display: block;
}
.whitepaper {
cursor: pointer;
text-align: center;
background-color: white;
border: 2px solid black;
border-radius: 3px;
float: left;
margin: 5px 5px 5px 0;
height: 40px;
width: 30px;
}
.blackframe {
text-align: center;
background-color: black;
cursor: pointer;
font-family: Tahoma, Geneva, sans-serif;
font-size:12px;
font-weight:bold;
margin: 12px 0 12px 0;
color: white;
width: 30px;
}
.whitepaper:hover {
cursor: pointer;
text-align: center;
background-color: black;
border: 2px solid white;
border-radius: 3px;
float: left;
margin: 5px 5px 5px 0;
height: 40px;
width: 30px;
}
 /* Tooltip container */
.tooltip {
  position: relative;
  display: inline-block;
}

/* Tooltip text */
.tooltip .tooltiptext {
  visibility: hidden;
  width: 120px;
  background-color: #555;
  color: #fff;
  text-align: center;
  padding: 5px 0;
  border-radius: 6px;

  /* Position the tooltip text */
  position: absolute;
  z-index: 1;
  bottom: 125%;
  left: 50%;
  margin-left: -60px;

  /* Fade in tooltip */
  opacity: 0;
  transition: opacity 0.3s;
}

/* Tooltip arrow */
.tooltip .tooltiptext::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  border-width: 5px;
  border-style: solid;
  border-color: #555 transparent transparent transparent;
}

/* Show the tooltip text when you mouse over the tooltip container */
.tooltip:hover .tooltiptext {
  visibility: visible;
  opacity: 1;
} 
</style>
</head>
<body>

<input type="checkbox" id="title1" name="contentbox" />
<label for="title1">Content 1</label>

<div class="content">

<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 1 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title1" id="myInput"></div>

</div>

<input type="checkbox" id="title2" name="contentbox" />
<label for="title2">Content 2</label>

<div class="content">

<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 2 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title2" id="myInput"></div>

</div>

<input type="checkbox" id="title3" name="contentbox" />
<label for="title3">Content 3</label>

<div class="content">

<div class="tooltip"><div class="whitepaper" onclick="myFunction()"><div class="blackframe"><span class="tooltiptext">Copy link 3 to clipboard</span>URL</div></div><input type="text" value="https://mywebsite.com/#title3" id="myInput"></div>

</div>

<script>
function myFunction() {
  /* Get the text field */
  var copyText = document.getElementById("myInput");

  /* Select the text field */
  copyText.select();
  copyText.setSelectionRange(0, 99999); /* For mobile devices */

   /* Copy the text inside the text field */
  navigator.clipboard.writeText(copyText.value);

  /* Alert the copied text */
  alert("Copied: " + copyText.value);
} 
</script>

</body>
</html>
```


Solution

  • UPDATE MAR 2023

    seems that the codepen broke at some point due to event.target returning the first child element instead of the clicked target. The solution for this was to use event.currentTarget to get the actual clicked element. Here's the new solution which is pretty similar to the older one but with some adjustments.

    function myFunction(event) {
      /* Get the text field */
      var copyText = event.currentTarget.firstElementChild.nextElementSibling.value
        
       /* Copy the text inside the text field */
      navigator.clipboard.writeText(copyText);
    
      /* Alert the copied text */
      alert("Copied: " + copyText);
    } 
    

    =========================================

    I found a few issues with your code

    1. You didn't change the id number on the inputs so they all would alert to the same URL which made it difficult to tell which is being clicked on.
    2. You are doing a query selection on an id that appears multiple times. This means it is not being fired on the clicked element.

    My approach includes taking advantage of the clicked element by passing it in your click handler.

        <div class="tooltip">
            <div class="whitepaper" onclick="myFunction(event)">
                <div class="blackframe"><span class="tooltiptext">Copy link 3 to clipboard</span>URL</div>
            </div><input type="text" value="https://mywebsite.com/#title3" id="myInput">
        </div>
    

    This lets me pass that event to the function call which will give us access to the current target node.

    function myFunction(event) {
      /* Get the text field */
        var copyText = event.target.parentNode.nextSibling.nextSibling.value
    
       /* Copy the text inside the text field */
      navigator.clipboard.writeText(copyText);
    
      /* Alert the copied text */
      alert("Copied: " + copyText);
    } 
    

    In the above case, I had to do some weird traversing because your input is outside the scope of the clicked element. I removed the code related to mobile stuff because that wasn't relevant to this issue (feel free to put that back in).

    here's the codepen with my example.