1

I have a for-each loop which prints out some text from a database and also displays a button. So essentially I have multiple "areas" of text and each with their own button. I want it so that whenever a user clicks on a button, that specific text it is matched up with is copied to the clipboard.

I tried doing multiple searches on this before posting on here. However, from the other solutions, I read they more or less show a solution for copying text in an input tag. However, in my case the user is not typing anything in that needs to get copied - the text is already on the screen and can't be changed.

I am having trouble with copying that specific text that matches a button since the text does not have a specific id or class name because it is just printing text as it goes through the loop, so they all have the same class name. So how would I specify a specific text with that button, since the buttons could get pressed in any order?

HTML:

<c:forEach items="${items}" var="item">
<h2>${item.itemDescription}</h2>
<input class="big-button" type="button" value="Add item copied to clipboard" id="btn" onclick="status(this)">
</c:forEach>

JavaScript:

function status(clickedBtn) 
  {
    clickedBtn.value = "Copied to clipboard";
    clickedBtn.disabled = true;
    clickedBtn.style.color = 'white';
    clickedBtn.style.background = 'gray';
  }
Poul Bak
  • 7,390
  • 4
  • 20
  • 40
daisygal
  • 33
  • 5
  • 2
    I've used this answer to get text onto the clipboard: https://stackoverflow.com/a/33928558/74757. You could use a selector to get the text from the `

    ` item and pass that.

    – Cᴏʀʏ Nov 11 '18 at 22:07
  • 1
    You could use the index of the item in the loop to generate an id too, so you can reference your element easier. – Cᴏʀʏ Nov 11 '18 at 22:13
  • @Cᴏʀʏ, thanks so much for linking that. So, I checked it out and it seems like they are able to do `new Date()` which copies the date to the clipboard. However, in my case, I have to copy the string that is is in the loop -- `${item.itemDescription}`. So, how would I be able to call that, since that is in my HTML, and not in the JS. – daisygal Nov 11 '18 at 22:14
  • @Cᴏʀʏ oh okay that makes sense. Sorry, I am still new to HTML and JS. how would I be able to reference a certain index, since I theoretically don't know how many elements there are since I am getting the information from the database and inserting into my HTML. – daisygal Nov 11 '18 at 22:15
  • What templating framework are you using? – Cᴏʀʏ Nov 11 '18 at 22:15
  • @Cᴏʀʏ I have this at the top of my HTML, `` which is allowing me to use that for each loop. – daisygal Nov 11 '18 at 22:16
  • I've written an answer that has a working example of everything tied together. – Cᴏʀʏ Nov 11 '18 at 22:33

3 Answers3

2

I would change your HTML to generate some kind of ID that can be used to reference the element with your text.

<c:forEach items="${items}" var="item" varStatus="loop">
    <h2 id="item-desc-${loop.index}">${item.itemDescription}</h2>
    <input class="big-button" data-desc-ref="item-desc-${loop.index}" type="button" value="Add item copied to clipboard" id="btn" onclick="status(this)">
</c:forEach>

Above I added code to generate a unique id (e.g. id="item-desc-0") on each <h2> element. On the button, I added a data- attribute to reference the ID of the <h2> so we can retrieve it later.

Now we change your status function to find the element by the ID specified in the data attribute, and get its innerText (the content between the opening and closing tags).

Then, you can pass that text to the function that copies it to the clipboard, reference above in my comment.

  function status(clickedBtn) 
  {
    var text = document.getElementById(clickedBtn.dataset.descRef).innerText;
    copyToClipboard(text);

    clickedBtn.value = "Copied to clipboard";
    clickedBtn.disabled = true;
    clickedBtn.style.color = 'white';
    clickedBtn.style.background = 'gray';
  }

Here's a working example. I had to put in sample HTML that would be generated by your loop.

function copyToClipboard(text) {
  if (window.clipboardData && window.clipboardData.setData) {
    // IE specific code path to prevent textarea being shown while dialog is visible.
    return clipboardData.setData("Text", text);

  } else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
    var textarea = document.createElement("textarea");
    textarea.textContent = text;
    textarea.style.position = "fixed"; // Prevent scrolling to bottom of page in MS Edge.
    document.body.appendChild(textarea);
    textarea.select();
    try {
      return document.execCommand("copy"); // Security exception may be thrown by some browsers.
    } catch (ex) {
      console.warn("Copy to clipboard failed.", ex);
      return false;
    } finally {
      document.body.removeChild(textarea);
    }
  }
}

function status(clickedBtn) {
  var text = document.getElementById(clickedBtn.dataset.descRef).innerText;

  copyToClipboard(text);

  clickedBtn.value = "Copied to clipboard";
  clickedBtn.disabled = true;
  clickedBtn.style.color = 'white';
  clickedBtn.style.background = 'gray';
}
<h2 id="item-desc-0">Testing 0</h2>
<input class="big-button" data-desc-ref="item-desc-0" type="button" value="Add item copied to clipboard" id="btn" onclick="status(this)">

<h2 id="item-desc-1">Testing 1</h2>
<input class="big-button" data-desc-ref="item-desc-1" type="button" value="Add item copied to clipboard" id="btn" onclick="status(this)">

<h2 id="item-desc-2">Testing 2</h2>
<input class="big-button" data-desc-ref="item-desc-2" type="button" value="Add item copied to clipboard" id="btn" onclick="status(this)">
Cᴏʀʏ
  • 97,417
  • 19
  • 158
  • 183
  • @Cory, thanks so much for this! I tried it and unfortunately, it's not working. When do you set the index number for the for-each loop? – daisygal Nov 11 '18 at 23:12
  • I supplied some modified JSTL code that renders the HTML a little differently. Did you remember to update your JSTL? – Cᴏʀʏ Nov 12 '18 at 20:10
  • @Cory, You deserve the goldest of stars!!! THIS WORKED!!! wow you are incredible!!! thank you so much!! :) – daisygal Nov 14 '18 at 23:47
  • You're welcome! Please study it and understand it, and reach back if any part of it didn't make sense. It will be beneficial to fully understand why it works so you can learn from it. – Cᴏʀʏ Nov 15 '18 at 01:32
1

If h2 element exactly is on previous of button you can use like this.

function status(clickedBtn) 
    {
      clickedBtn.value = "Copied to clipboard";
      clickedBtn.disabled = true;
      clickedBtn.style.color = 'white';
      clickedBtn.style.background = 'gray';

      //New Code
      copyToCliboard(clickedBtn.previousSibling);
    }
    function copyToCliboard(el) {
      if (document.body.createTextRange) {
          var range = document.body.createTextRange();
          range.moveToElementText(el);
          range.select();
      } else {
          var selection = window.getSelection();
          var range = document.createRange();
          range.selectNodeContents(el);
          selection.removeAllRanges();
          selection.addRange(range);
      }
      document.execCommand("copy");
      window.getSelection().removeAllRanges();
  }
h2{
display:inline;
}
<html>
  <head>
 </head>
  <body>
   <div><h2>Text1</h2><button onclick="status(this)">Copy</button></div>
   <div><h2>Text2</h2><button onclick="status(this)">Copy</button></div>
  </body>
</html>
RGhanbari
  • 124
  • 5
0

Cory provides a very helpful link to the clipboard aspect, I thought I'd provide the info about how to target a clicked element.

To achieve this, pass event to your onclick attribute, and use event.target to reference the clicked element. Here's a basic example:

function copy(e) {
 alert(e.target.value)
}
<button onclick="copy(event)" value="copy this">copy this</button>
<button onclick="copy(event)" value="copy that">copy that</button>

To address your specific requirement, if you put item.itemDescription into the value attribute, then it will copy the text you want.

Frish
  • 1,105
  • 6
  • 16
  • thanks so much! So since I already have something in my `value` attribute, is there another way around this? – daisygal Nov 11 '18 at 22:26
  • @daisygal sure, you could probably do something like use a [parent selector](https://www.w3schools.com/jsref/prop_node_parentelement.asp) and then find the `h2` value from there. – Frish Nov 11 '18 at 22:32