2

I need to copy the email address when someone clicks on the associated image.

I had it all written in a html file, but now I'm transferring my code in a .js so I can use JSON to store the staff data instead of writing them one by one. The problem is that I had a function that was called when the image is clicked that doesn't work when I use buildStaff.

I had it written like this:

<img class="imageCopy" id="copy-button-${ID}" src="copyIcon.png" onclick="copyEmail('email-1', 'toolTip-1')">

And I have this now :

function buildStaff(staff, ID) {
  return `
    <div class="staffGroup">
        <span class="staffTextGroup">
            <p class="name"> ${staff.name} <\p>
            <span class="email" id="email-${ID}"> ${staff.email} <\span>
        </span>
        <span class="copyButton">
            <span class="tootTipText" id="tootTip-${ID}"> Copied. <\span>
            <img class="imageCopy" id="copy-button-${ID}" src="copyIcon.png">
        <\span>
    </div>
  `;
}

I just can't find a way to use the dynamic variables for the copyEmail function.

Thank you very much! This is my first time doing a website, so I'm a bit lost.

EDIT

This is the complete original JS code, but without the onclick="copyEmail() in the img.

$(document).ready(() => {
    let staffAnchor = $('[staff-anchor]');

    fetchFromJson(STAFF_JSON_FILE_PATH, StaffData => {
        StaffData.staff.forEach((staff, i) => {

            const ID = `staff-${i}`;
                
            let staffElement = buildStaff(staff, ID);
            $(staffAnchor).append(staffElement);
        });
    });
});

function fetchFromJson(jsonFilePath, callback) {
    $.getJSON(jsonFilePath, callback);
}

function buildStaff(staff, ID) {
    return `
        <div class="staffGroup">
            <span class="staffTextGroup">
                <p class="name"> ${staff.name} <\p>
                <span class="email" id="email-${ID}"> ${staff.email} <\span>
            </span>
            <span class="copyButton">
                <span class="tootTipText" id="tootTip-${ID}"> Copied. <\span>
                <img class="imageCopy" id="copy-button-${ID}" src="copyIcon.png" alt="copy image">
            <\span>
        </div>
    `;
}

function copyEmail(id, tooltip) {
    var r = document.createRange();
    r.selectNode(document.getElementById(id));
    window.getSelection().removeAllRanges();
    window.getSelection().addRange(r);
    document.execCommand('copy');
    window.getSelection().removeAllRanges();
}

The problem is that I can't find a way to use copyEmail() now that I can't simply write it in the HTML.

  • 1
    Please show codes for button generation and `copyEmail` function – Swati Feb 27 '21 at 04:39
  • "It doesn't work" doesn't make a good question. If you can tell us what you expect your code to do and what it actually does (as well as provide all the relevent code), we may be better able to help. – Cat Feb 27 '21 at 04:42

2 Answers2

1

Responding to user actions in dynamically added markup is one of the situations where it helps to distinguish between HTML elements and DOM elements. We add a string of HTML using, say, the .innerHTML property, and JS parses the string to build a corresponding subtree of DOM nodes; then we can select and manipulate any of them with methods like .getElementById.)

In this case, we want an event listener to access some elements a few levels down in the new tree -- particularly an img that serves as a button, and a span that holds the staff member's email address.

This solution returns a reference to the new DOM element from buildStaff so we can add the copyEmail function as a click lister on it. The img element is distinguished by the copyEmailBtn class, and, similarly, the span gets a emailEl class so JavaScript can know where to find the email.

(In the snippet I just log the email to show that the listener can access it, but of course you can do anything you like with the value: cache it in the browser, send it to the server, use it to send an email, etc.)

// Identifies container element and defines data 
const
  container = document.getElementById("container"),
  myId = "myId",
  spock = {
    name: "Spock",
    email: "spock@starfleet.org"
  };

// Makes staff element, using the above info
const staffEl = buildStaff(spock, myId, container);

// Calls copyEmail whenever user clicks in staffEl
staffEl.addEventListener("click", copyEmail);

// Defines `copyEmail`
function copyEmail(event){
  const
    staffEl = event.currentTarget, // Element w/ listener
    clickedThing = event.target; // Clicked descendant
 
  // Ignores irrelevant clicks 
  if(!clickedThing.classList.contains("copyEmailBtn")){
  return;
}
  // Finds email within staffEl, does something with it
  const
    emailEl = staffEl.getElementsByClassName("emailEl")[0],
    email = emailEl.textContent.trim();
  console.log("Copied email:", email);
}

// Defines `buildStaff` (`parentEl` will contain the new div)
function buildStaff(staff, ID, parentEl) {
  const
    // Destructures staff obj to make local variables
    {name, email} = staff,

    // Builds strings using `ID` and binds them to variables
    emailId = `email-${ID}`,
    buttonId = `copy-button-${ID}`;

  // Defines new HTML (w/ emailEl & copyEmailBtn classes)
  const newHTML = `
    <div class="narrow">
      <div class="text">
        <div> ${name} </div>
        <div class="emailEl" id="${emailId}"> ${email} </div>
      </div>
      <div class="button">
        <span> Click to copy email → </span> 
        <span><img class="copyEmailBtn" id="${buttonId}" src="copyIcon.png"></span>
      </div>
    </div>
    `;
  // Inserts new HTML
  parentEl.innerHTML = newHTML;
  
  // Returns the DOM element representing the new <div>
  const staffEl = container.children[0];
  return staffEl;
}
.narrow{ width: 200px; }
.text{ padding: 4px; border: 1px solid lightgrey; }
img{ margin-left: 10px; }
.button{ margin-top: 10px; font-weight: bold; }
<div id="container"></div>

(Note: It seems that interpolation in JavaScript template literals doesn't play nicely with object properties accessed via the dot notation, so I copied the name and email values from the staff object before sticking them into the HTML.)

Cat
  • 4,141
  • 2
  • 10
  • 18
  • Thank you so much! I have a question : what would be my equivalent for `container`? (I edited my post) I thought it would be `staffAnchor`, but nothing shows on my page. ` $(document).ready(() => { let staffAnchor = $('[staff-anchor]'); fetchFromJson(STAFF_JSON_FILE_PATH, StaffData => { StaffData.staff.forEach((staff, i) => { const staffId = `staff-${i}`; let staffElement = buildStaff(staff, staffId, $(staffAnchor)); staffElement.addEventListener("click", copyEmail); }); }); }); ` – ChrEliGroux Feb 27 '21 at 15:30
  • Sorry, I can't find how to write my code in block in the previous comment! – ChrEliGroux Feb 27 '21 at 15:34
  • 1
    Have you had any success with this? I'm not positive without seeing your HTML, but yes, I think `staffAnchor` is the correct parent element. I would have expected to see `buildStaff(staff, staffId, staffAnchor)` instead of `buildStaff(staff, staffId, $(staffAnchor))`, so that might be the problem, but I don't use jQuery much, so I may be missing something. Btw, if you haven't done so already, you can also try adding `console.log` statements at each step of the code to make sure your values are what you expect (and wherever something weird gets logged, you can learn what is causing the error.) – Cat Mar 03 '21 at 23:37
0

I need to copy the email address when someone clicks on the associated image.

copy to clipboard works only on input elements which has .value property and you can't use the .execCommand on elements like <div>, <p>, <span>... which has .innerHTML property.

A workaround is to create a temporary textarea or input element and pass the div's innerHTML value inorder to copy its content.

Suppose we have an email john_doe@gmail.com and its Associated image copyIcon.png

Assuming all associated email address tags contains a unique id, then we can pass the id to the function copyEmail() to copy the contents.

function copyEmail(mail_id){
    var copyText = document.getElementById(mail_id).textContent;
    var textarea = document.createElement('textarea');
    textarea.id = 'tmp_elmnt';
    textarea.style.display = "none";
    document.body.appendChild(textarea);
    textarea.value = copyText;
    var selector = document.getElementById('tmp_elmnt');
    selector.select();
    document.execCommand('copy');
    alert("Copied mail : " + copyText);
    document.body.removeChild(textarea);
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>click on Associated image to copy its respective email address</h3>
    <span id="mail1">john_doe@gmail.com</span><img src="copyIcon.png" alt="" onclick="copyEmail('mail1')">
    <br>
    <span id="mail2">jane_doe@gmail.com</span><img src="copyIcon.png" alt="" onclick="copyEmail('mail2')">
    <br>
    <span id="mail3">johnny_doe@gmail.com</span><img src="copyIcon.png" alt="" onclick="copyEmail('mail3')">
    <br>
    <span id="mail4">jancy_doe@gmail.com</span><img src="copyIcon.png" alt="" onclick="copyEmail('mail4')">

</body>
</html>
A5H1Q
  • 544
  • 7
  • 15