javascriptevent-listenertemplate-literalshtml-templates

How to add event listener with HTML template literals in Javascript?


I have fetched some data from a URL and after that I have generated an HTML template for each item of received data using a forEach loop. After creating the HTML template for all the elements in response data I have assigned that to document.body.innerHTML. So it displays all elements with my HTML template.

The response has 30 objects and each object has a link (clone_url) with other information. I have generated HTML elements for those 30 objects using my HTML template, each has a name, profile Avatar and button to copy the specific link I have mentioned. I want to copy that link into the clipboard when user click the copy button.

Therefore, I want to add event listeners to the buttons. That is where I have been struggling because I have no idea how can I do that. Because I have to access each component and also I want to access related response object to get the link.

const url = "https://api.github.com/users/shrutikapoor08/repos";

fetch(url)
  .then((response) => response.json())
  .then((users) => {
    console.log(users);
    let htmlText = "";
    users.forEach((i) => {
      htmlText += `
        <div class="repo-container">
        <p class="repo-title">${i.name}</p>
        <div class="repo-owner">
          <img
            src="${i.owner.avatar_url}"
            alt="avatar"
            class="avatar"
          />
          <a href="${i.owner.html_url}" class="repo-owner-username">${i.owner.login}</a>
        </div>
        <div class="link-container">
          <button class="repo-link-btn">Copy</button>
          <p class="tooltip">Link Coppied</p>
        </div>
      </div>
        `;
    });
    document.body.innerHTML = htmlText;
  });
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap");

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: "Roboto", sans-serif;
}

.repo-container {
  background-color: rgb(255, 253, 251);
  border: 1px solid rgb(226, 226, 226);
  padding: 15px;
  width: 250px;
  margin: 10px;
}

.repo-title {
  font-weight: bold;
  margin-bottom: 10px;
}

.repo-owner {
  display: flex;
  align-items: center;
}

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  margin-right: 20px;
}

.repo-owner-username {
  font-size: 0.8rem;
  text-decoration: none;
}

.tooltip {
  background-color: rgba(0, 0, 0, 0.7);
  width: fit-content;
  padding: 4px 10px;
  border-radius: 10px;
  font-size: 0.75rem;
  color: white;
  opacity: 0;
  transition: transform 0.3s ease, opacity 0.3s ease;
  pointer-events: none;
}

.tooltip.active {
  opacity: 1;
  transform: translateY(-10px);
}

.link-container {
  display: flex;
}

.repo-link-btn {
  margin: 10px 0px;
}
<!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" />
    <link rel="stylesheet" href="./style.css" />
    <title>Document</title>
  </head>
  <body>
    <script src="./app.js"></script>
  </body>
</html>

This is the code I want to add with each button.

document.querySelector(".repo-link-btn").addEventListener("click", async () => {
  const text = "";
  await navigator.clipboard.writeText(text);
  document.querySelector(".tooltip").classList.add("active");
  setTimeout(() => {
    document.querySelector(".tooltip").classList.remove("active");
  }, 500);
});

const text should be equal to the response data objects URL. I have to access that data as well. What is the good practice for solving this problem?


Solution

  • If you want to use pure JavaScript you need to create loop and add event listener to all item like this:

    [...document.querySelectorAll(".repo-link-btn")].forEach(function (item) {..}
    

    And to find clone_url value you can set custom attribute like data-cloneUrl="${i.clone_url}" and then get it in click time: e.target.getAttribute("data-cloneUrl");

    Note that this code is based on your html and pure JavaScript. There is more easy way by jQuery.

            const url = "https://api.github.com/users/shrutikapoor08/repos";
    
            fetch(url)
                .then((response) => response.json())
                .then((users) => {
                    console.log(users);
                    let htmlText = "";
                    users.forEach((i) => {
                        htmlText += `
            <div class="repo-container">
            <p class="repo-title">${i.name}</p>
            <div class="repo-owner">
              <img
                src="${i.owner.avatar_url}"
                alt="avatar"
                class="avatar"
              />
              <a href="${i.owner.html_url}" class="repo-owner-username">${i.owner.login}</a>
            </div>
            <div class="link-container">
              <button class="repo-link-btn" data-cloneUrl="${i.clone_url}">Copy</button>
              <p class="tooltip">Link Coppied</p>
            </div>
          </div>
            `;
                    });
                    document.body.innerHTML = htmlText;
    
                    [...document.querySelectorAll(".repo-link-btn")].forEach(function (item) {
                        item.addEventListener("click", async (e) => {
                            const text = e.target.getAttribute("data-cloneUrl");
                            await navigator.clipboard.writeText(text);
                            document.querySelector(".tooltip").classList.add("active");
                            setTimeout(() => {
                                document.querySelector(".tooltip").classList.remove("active");
                            }, 500);
                        });
                    })
                });
    @import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap");
    
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
    
            body {
                font-family: "Roboto", sans-serif;
            }
    
            .repo-container {
                background-color: rgb(255, 253, 251);
                border: 1px solid rgb(226, 226, 226);
                padding: 15px;
                width: 250px;
                margin: 10px;
            }
    
            .repo-title {
                font-weight: bold;
                margin-bottom: 10px;
            }
    
            .repo-owner {
                display: flex;
                align-items: center;
            }
    
            .avatar {
                width: 30px;
                height: 30px;
                border-radius: 50%;
                margin-right: 20px;
            }
    
            .repo-owner-username {
                font-size: 0.8rem;
                text-decoration: none;
            }
    
            .tooltip {
                background-color: rgba(0, 0, 0, 0.7);
                width: fit-content;
                padding: 4px 10px;
                border-radius: 10px;
                font-size: 0.75rem;
                color: white;
                opacity: 0;
                transition: transform 0.3s ease, opacity 0.3s ease;
                pointer-events: none;
            }
    
                .tooltip.active {
                    opacity: 1;
                    transform: translateY(-10px);
                }
    
            .link-container {
                display: flex;
            }
    
            .repo-link-btn {
                margin: 10px 0px;
            }

    Note that the snippet worked correctly But stack overflow snippet said The Clipboard API has been blocked because of a permissions policy applied to the current document. So check the snippet in your computer.