1

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?

2
  • Event delegation. Add one listener to a parent element rather than lots of listeners to lots of elements. The events bubble up the DOM to the parent listener which catches them. You can then determine what action to take based on the information in the event. Commented Aug 8, 2021 at 8:26
  • @Andy But finally how can I access fetched response data to get that specific link to each and every component? Because inside the event listener I wanna access related response data. 🤔 Commented Aug 8, 2021 at 11:11

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

9 Comments

@Kasun Has your problem been solved? let me know if there is a problem
Yes this is working! 🔥 But I don't understand how it works. I don't understand how it access response data object after foreach loop. e.target.parentElement.previousElementSibling.getElementsByTagName('a')[0].getAttribute("href"); How this thing access that url.
@Kasun Don't worry I explain for you. When you create an event the callback function parameter is event (say e here). and e.target is the target that you click on it. here is button. But your link is in the previous sibling of parent of button :) . I mean div with class "repo-owner"
@Kasun So first I get the parent of button that is div with link-container class and then I try to access previousElementSibling which is div with repo-owner class. e.target.parentElement.previousElementSibling
The final stage is get anchor tag from repo-owner class which is: getElementsByTagName('a') . I hope my explanation be clear for you
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.