Create copy buttons alert from javascript

159 Views Asked by At

Hey am new web developer and am working on a quote website project.

I have created a Html page in which there are some quotes with a copy button below all quotes.

You can visit my CodePen to understand the question clearly https://codepen.io/Akash11166666/pen/JjRzqzp

(I have displayed copy buttons and quotes from Javascript so you cannot find the elements in HTML.)

As you can see my CodePen they work perfectly but we cannot know if the text is copied or not. So I need a <span>Copied</span> to be visible when each copy button is clicked without inserting the <span>Copied</span> to HTML directly, that is from javascript same as copy button was created. They should be so dynamic that for respective copy button thespan element should be visible and invisible after some seconds.

I have tried numerous way to solve this problem but know of them work although am new to javascript and I don't know much about it.

My CodePen project for reference

const resultEl = document.querySelector('.allquotes');
const pageSize = document.querySelector('select[name="page-size"]');
const pageCurr = document.querySelector('input[name="page-curr"]')
const resultCount = document.querySelector('.result-count')
const pageNoCurr = document.querySelector('.page-no-curr');
const pageNoCount = document.querySelector('.page-no-count')
const btnFirst = document.querySelector('.page-btn-first');
const btnPrev = document.querySelector('.page-btn-prev');
const btnNext = document.querySelector('.page-btn-next');
const btnLast = document.querySelector('.page-btn-last');
let results = [];
const getResultCount = () => results.length;
const getPageSize = () => +pageSize.value;
const getCurrPage = () => +pageCurr.value;
const getPageCount = () => Math.ceil(getResultCount() / getPageSize());
const pageResponse = (records, pageSize, page) =>
  (start => records.slice(start, Math.min(records.length, start + pageSize)))
    (pageSize * (page - 1));
const main = async () => {
  btnFirst.addEventListener('click', navFirst);
  btnPrev.addEventListener('click', navPrev);
  btnNext.addEventListener('click', navNext);
  btnLast.addEventListener('click', navLast);
  pageSize.addEventListener('change', changeCount);
  results = await retrieveAllQuotes();
  updatePager(results);
  redraw();
};
const redraw = () => {
  resultEl.innerHTML = '';
  const paged = pageResponse(results, getPageSize(), getCurrPage());
  const contents = document.createElement('div');
  contents.innerHTML = paged.map(record => `<div class='latestatus'><p class='copytxt'>${record.quotes}</p><div> <button class="copystatus btn">Copy</button></div></div>`).join('');
  resultEl.append(contents);
};
const navFirst = (e) => {
  pageNoCurr.textContent = 1;
  pageCurr.value = 1;
  redraw();
}
const navPrev = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const prevPage = curr > 1 ? curr - 1 : curr;
  pageCurr.value = prevPage;
  pageNoCurr.textContent = prevPage;
  redraw();
}
const navNext = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const nextPage = curr < pages ? curr + 1 : curr;
  pageCurr.value = nextPage;
  pageNoCurr.textContent = nextPage;
  redraw();
}
const navLast = (e) => {
  pageNoCurr.textContent = getPageCount();
  pageCurr.value = getPageCount();
  redraw();
}
const changeCount = () => {
  updatePager();
  redraw();
};
const updatePager = () => {
  const count = getPageCount();
  const curr = getCurrPage();
  pageCurr.value = curr > count ? 1 : curr;
  pageNoCurr.textContent = curr > count ? 1 : curr;
  pageNoCount.textContent = count;
  resultCount.textContent = getResultCount();
};
const retrieveAllQuotes = async function () {
  // write your asynchronous fetching here
  return [{
    quotes: "1The cat is better than dog."
  },
  {
    quotes: "2Google is a open source library."
  },
  {
    quotes: "3Cats are better than ferrets."
  },
  {
    quotes: "4Love books."
  },
  {
    quotes: "5Life is short make it possible."
  },
  {
    quotes: "6The cat is better than dog"
  },
  {
    quotes: "7Google is a open source library."
  },
  {
    quotes: "8Cats are better than ferrets."
  },
  {
    quotes: "9Love books."
  },
  {
    quotes: "10Life is short make it possible."
  },
  ];
}
document.querySelector('.allquotes').addEventListener(
  'click',
  function (e) {
    e.preventDefault();
    if (e.target && e.target.matches('.copystatus')) {
      const quote = e.target.parentNode.closest('.latestatus')
        .childNodes[0].textContent;
      const textArea = document.createElement('textarea');
      textArea.value = quote;
      document.body.appendChild(textArea);
      textArea.select();
      document.execCommand('Copy');
      textArea.remove();
    }
  },
  false
);
main();
.mainStatus {
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  padding-bottom: 5px;
  margin: 10px;
  margin-top: 10px;
  max-width: 95%;
  width: 95%;
  height: auto;
  border-radius: 20px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}

.statusHeading {
  text-align: center;
  background-color: #18b495;
  color: #ffffff;
  padding: 10px 10px 10px 10px;
  border-top-right-radius: 20px;
  border-top-left-radius: 20px;
  font-weight: 300;
  font-size: 20px;
}

.latestatus {
  display: grid;
  height: auto;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  padding: 10px 20px 10px 20px;
  border-radius: 30px;
  margin: 10px 10px 10px 10px;
  width: 445px;
  min-height: 130px;
  font-size: 15px;
}

.allStatus {
  display: flex;
}

.latestatus p {
  width: auto;
  position: relative;
}

.copystatus {
  font-weight: 500;
  text-transform: uppercase;
  width: 100px;
  height: 40px;
}

.pagable {
  display: flex;
  flex-direction: column;
  border: var(--pageable-border);
  background: var(--pageable-background);
}

.pagable .pagable-results {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0.25em;
}

.pagable .pagable-status {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 0.25em;
  background: var(--pageable-status-background);
}

.pagable .pagable-actions {
  display: grid;
  grid-auto-flow: column;
  grid-gap: 0.25em;
}

.pagable .pagable-actions input[name="page-curr"] {
  width: 3em;
}
<!DOCTYPE html>
<html>

<head>
  <style>
    /* Main Status */
  </style>
</head>

<body>
  <a href="hindinj.html">caeman</a>
  <div class="mainStatus">
    <h2 class="statusHeading">Latest English Status</h2>
    <div class="allquotes"></div>
    <div class="pagable-status">
      <label>Page <span class="page-no-curr">1</span> of <span class="page-no-count">1</span></label>
      <div class="pagable-actions">
        <button class="page-btn-first">&#x226A;</button>
        <button class="page-btn-prev">&#60;</button>
        <input type="number" name="page-curr" min="1" value="1" />
        <button class="page-btn-next">&#62;</button>
        <button class="page-btn-last">&#x226B;</button>
        <select name="page-size">
          <option>20</option>
          <option>10</option>
          <option>20</option>
        </select>
      </div>
      <label>(<span class="result-count"></span> items)</label>
    </div>

    <script>
    </script>
</body>

</html>

Again I would tell my problem that, I need a <span>Copied</span> to be visible when each copy button is clicked without inserting the <span>Copied</span> to HTML directly, that is from javascript same as copy button was created.

I hartley thanks for those who answer this question.

2

There are 2 best solutions below

0
On

You can do this by calling a function right after your call to document.execCommand('Copy'). Check my snippet below for showCopied and CSS class hasCopied. This should temporarily show the span and using a setTimeout it can be removed after some given time in milliseconds (2000 = 2 seconds).

const resultEl = document.querySelector('.allquotes');
const pageSize = document.querySelector('select[name="page-size"]');
const pageCurr = document.querySelector('input[name="page-curr"]')
const resultCount = document.querySelector('.result-count')
const pageNoCurr = document.querySelector('.page-no-curr');
const pageNoCount = document.querySelector('.page-no-count')
const btnFirst = document.querySelector('.page-btn-first');
const btnPrev = document.querySelector('.page-btn-prev');
const btnNext = document.querySelector('.page-btn-next');
const btnLast = document.querySelector('.page-btn-last');

let results = [];

const getResultCount = () => results.length;
const getPageSize = () => +pageSize.value;
const getCurrPage = () => +pageCurr.value;
const getPageCount = () => Math.ceil(getResultCount() / getPageSize());

const pageResponse = (records, pageSize, page) =>
  (start => records.slice(start, Math.min(records.length, start + pageSize)))
  (pageSize * (page - 1));

const main = async() => {
  btnFirst.addEventListener('click', navFirst);
  btnPrev.addEventListener('click', navPrev);
  btnNext.addEventListener('click', navNext);
  btnLast.addEventListener('click', navLast);
  pageSize.addEventListener('change', changeCount);

  results = await retrieveAllQuotes();
  updatePager(results);
  redraw();
};
const redraw = () => {
  resultEl.innerHTML = '';
  const paged = pageResponse(results, getPageSize(), getCurrPage());
  const contents = document.createElement('div');
  contents.innerHTML = paged.map(record => `<div class='latestatus'><p class='copytxt'>${record.quotes}</p><div> <button class="copystatus btn">Copy</button></div></div>`).join('');
  resultEl.append(contents);
};

const navFirst = (e) => {
  pageNoCurr.textContent = 1;
  pageCurr.value = 1;
  redraw();
}

const navPrev = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const prevPage = curr > 1 ? curr - 1 : curr;
  pageCurr.value = prevPage;
  pageNoCurr.textContent = prevPage;
  redraw();
}

const navNext = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const nextPage = curr < pages ? curr + 1 : curr;
  pageCurr.value = nextPage;
  pageNoCurr.textContent = nextPage;
  redraw();
}

const navLast = (e) => {
  pageNoCurr.textContent = getPageCount();
  pageCurr.value = getPageCount();
  redraw();
}

const changeCount = () => {
  updatePager();
  redraw();
};

const updatePager = () => {
  const count = getPageCount();
  const curr = getCurrPage();
  pageCurr.value = curr > count ? 1 : curr;
  pageNoCurr.textContent = curr > count ? 1 : curr;
  pageNoCount.textContent = count;
  resultCount.textContent = getResultCount();
};

const retrieveAllQuotes = async function() {

  // write your asynchronous fetching here

  return [{
      quotes: "1The cat is better than dog."
    },
    {
      quotes: "2Google is a open source library."
    },
    {
      quotes: "3Cats are better than ferrets."
    },
    {
      quotes: "4Love books."
    },
    {
      quotes: "5Life is short make it possible."
    },
    {
      quotes: "6The cat is better than dog"
    },
    {
      quotes: "7Google is a open source library."
    },
    {
      quotes: "8Cats are better than ferrets."
    },
    {
      quotes: "9Love books."
    },
    {
      quotes: "10Life is short make it possible."
    },
  ];
}
document.querySelector('.allquotes').addEventListener(

  'click',

  function(e) {

    e.preventDefault();

    if (e.target && e.target.matches('.copystatus')) {

      const quote = e.target.parentNode.closest('.latestatus')

        .childNodes[0].textContent;

      const textArea = document.createElement('textarea');

      textArea.value = quote;

      document.body.appendChild(textArea);

      textArea.select();

      document.execCommand('Copy');

      showCopied();

      textArea.remove();

    }

  },

  false

);

function showCopied() {
  const copiedElement = document.createElement('span');
  copiedElement.innerHTML = "Copied";
  copiedElement.setAttribute("class", "hasCopied");
  resultEl.appendChild(copiedElement);
  setTimeout(() => {
    resultEl.removeChild(copiedElement);
  }, 2000);
}

main();
/* Main Status */

.mainStatus {
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  padding-bottom: 5px;
  margin: 10px;
  margin-top: 10px;
  max-width: 95%;
  width: 95%;
  height: auto;
  border-radius: 20px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}

.statusHeading {
  text-align: center;
  background-color: #18b495;
  color: #ffffff;
  padding: 10px 10px 10px 10px;
  border-top-right-radius: 20px;
  border-top-left-radius: 20px;
  font-weight: 300;
  font-size: 20px;
}

.latestatus {
  display: grid;
  height: auto;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
  padding: 10px 20px 10px 20px;
  border-radius: 30px;
  margin: 10px 10px 10px 10px;
  width: 445px;
  min-height: 130px;
  font-size: 15px;
}

.allStatus {
  display: flex;
}

.latestatus p {
  width: auto;
  position: relative;
}

.copystatus {
  font-weight: 500;
  text-transform: uppercase;
  width: 100px;
  height: 40px;
}

.pagable {
  display: flex;
  flex-direction: column;
  border: var(--pageable-border);
  background: var(--pageable-background);
}

.pagable .pagable-results {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0.25em;
}

.pagable .pagable-status {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 0.25em;
  background: var(--pageable-status-background);
}

.pagable .pagable-actions {
  display: grid;
  grid-auto-flow: column;
  grid-gap: 0.25em;
}

.pagable .pagable-actions input[name="page-curr"] {
  width: 3em;
}

.hasCopied {
  color: black;
  position: absolute;
  top: 30%;
  right: 10%;
}
<!DOCTYPE html>
<html>

<body>
  <a href="hindinj.html">caeman</a>
  <div class="mainStatus">
    <h2 class="statusHeading">Latest English Status</h2>
    <div class="allquotes"></div>
    <div class="pagable-status">
      <label>Page <span class="page-no-curr">1</span> of <span class="page-no-count">1</span></label>
      <div class="pagable-actions">
        <button class="page-btn-first">&#x226A;</button>
        <button class="page-btn-prev">&#60;</button>
        <input type="number" name="page-curr" min="1" value="1" />
        <button class="page-btn-next">&#62;</button>
        <button class="page-btn-last">&#x226B;</button>
        <select name="page-size">
          <option>20</option>
          <option>10</option>
          <option>20</option>
        </select>
      </div>
      <label>(<span class="result-count"></span> items)</label>
    </div>
</body>

</html>

0
On

You can add to your button a function onclick and whenever it's clicked it will add and remove the span after x seconds using setTimout().

Also i have added a class on css for styling the span but you can always remove it or edit it.

The code i have added was on js only was the function showCopied() which will create the element and add to the body. Also added fade() and unfade() methods which will do the fade and unfade of the element this 2 methods use setInterval().

const resultEl = document.querySelector('.allquotes');
const pageSize = document.querySelector('select[name="page-size"]');
const pageCurr = document.querySelector('input[name="page-curr"]')
const resultCount = document.querySelector('.result-count')
const pageNoCurr = document.querySelector('.page-no-curr');
const pageNoCount = document.querySelector('.page-no-count')
const btnFirst = document.querySelector('.page-btn-first');
const btnPrev = document.querySelector('.page-btn-prev');
const btnNext = document.querySelector('.page-btn-next');
const btnLast = document.querySelector('.page-btn-last');

let results = [];

const getResultCount = () => results.length;
const getPageSize = () => +pageSize.value;
const getCurrPage = () => +pageCurr.value;
const getPageCount = () => Math.ceil(getResultCount() / getPageSize());

const pageResponse = (records, pageSize, page) =>
  (start => records.slice(start, Math.min(records.length, start + pageSize)))
  (pageSize * (page - 1));

const main = async() => {
  btnFirst.addEventListener('click', navFirst);
  btnPrev.addEventListener('click', navPrev);
  btnNext.addEventListener('click', navNext);
  btnLast.addEventListener('click', navLast);
  pageSize.addEventListener('change', changeCount);

  results = await retrieveAllQuotes();
  updatePager(results);
  redraw();
};
const redraw = () => {
  resultEl.innerHTML = '';
  const paged = pageResponse(results, getPageSize(), getCurrPage());
  const contents = document.createElement('div');
  contents.innerHTML = paged.map(record => `<div class='latestatus'><p class='copytxt'>${record.quotes}</p><div> <button class="copystatus btn" onclick="showCopied();">Copy</button></div></div>`).join('');
  resultEl.append(contents);
};

function showCopied() {
  let spanCopied = document.createElement('span');
  spanCopied.innerHTML = "Copied!";
  spanCopied.classList.add("spanCopied");
  document.body.appendChild(spanCopied);
  fade(spanCopied);
  
  setTimeout(function() {
    document.body.removeChild(spanCopied);
  }, 1400);
}

function fade(element) {
    var op = 1;  // initial opacity
    var timer = setInterval(function () {
        if (op <= 0.1){
            clearInterval(timer);
            element.style.display = 'none';
        }
        element.style.opacity = op;
        element.style.filter = 'alpha(opacity=' + op * 100 + ")";
        op -= op * 0.1;
    }, 50);
}

function unfade(element) {
    var op = 0.1;  // initial opacity
    element.style.display = 'block';
    var timer = setInterval(function () {
        if (op >= 1){
            clearInterval(timer);
        }
        element.style.opacity = op;
        element.style.filter = 'alpha(opacity=' + op * 100 + ")";
        op += op * 0.1;
    }, 10);
}

const navFirst = (e) => {
  pageNoCurr.textContent = 1;
  pageCurr.value = 1;
  redraw();
}

const navPrev = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const prevPage = curr > 1 ? curr - 1 : curr;
  pageCurr.value = prevPage;
  pageNoCurr.textContent = prevPage;
  redraw();
}

const navNext = (e) => {
  const pages = getPageCount();
  const curr = getCurrPage();
  const nextPage = curr < pages ? curr + 1 : curr;
  pageCurr.value = nextPage;
  pageNoCurr.textContent = nextPage;
  redraw();
}

const navLast = (e) => {
  pageNoCurr.textContent = getPageCount();
  pageCurr.value = getPageCount();
  redraw();
}

const changeCount = () => {
  updatePager();
  redraw();
};

const updatePager = () => {
  const count = getPageCount();
  const curr = getCurrPage();
  pageCurr.value = curr > count ? 1 : curr;
  pageNoCurr.textContent = curr > count ? 1 : curr;
  pageNoCount.textContent = count;
  resultCount.textContent = getResultCount();
};

const retrieveAllQuotes = async function() {

  // write your asynchronous fetching here

  return[{
      quotes: "1The cat is better than dog."
    },
    {
      quotes: "2Google is a open source library."
    },
    {
      quotes: "3Cats are better than ferrets."
    },
    {
      quotes: "4Love books."
    },
    {
      quotes: "5Life is short make it possible."
    },
    {
      quotes: "6The cat is better than dog"
    },
    {
      quotes: "7Google is a open source library."
    },
    {
      quotes: "8Cats are better than ferrets."
    },
    {
      quotes: "9Love books."
    },
    {
      quotes: "10Life is short make it possible."
    },
]; 
}
document.querySelector('.allquotes').addEventListener(

  'click',

  function (e) {

    e.preventDefault();

    if (e.target && e.target.matches('.copystatus')) {

        const quote = e.target.parentNode.closest('.latestatus')

            .childNodes[0].textContent;

        const textArea = document.createElement('textarea');

        textArea.value = quote;

        document.body.appendChild(textArea);

        textArea.select();

        document.execCommand('Copy');

        textArea.remove();

    }

  },

  false

);
main();
/* Main Status */
body {
  position: relative;
}
.mainStatus{
 background-color: #fff;
 border-radius: 10px;
 box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
 padding-bottom: 5px;
 margin: 10px;
 margin-top: 10px;
 max-width: 95%;
 width: 95%;
 height: auto;
 border-radius: 20px;
 box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
}
.spanCopied {
  background-color: #f2f2f2;
  border: 1px solid #f2f2f2;
  border-radius: 50px;
  position: fixed;
  left: 0;
  right: 0;
  top: 80vh;
  margin: auto;
  padding: 10px;
  z-index: 3;
  max-width: 90px;
  height: 20px;
  text-align: center;
  box-shadow: 0 1px 9px 0 rgba(0,0,0,.2);
  transition: .3s;
  opacity: 0;
}
.statusHeading{
 text-align: center;
 background-color: #18b495;
 color: #ffffff;
 padding: 10px 10px 10px 10px;
 border-top-right-radius: 20px;
 border-top-left-radius: 20px;
 font-weight: 300;
 font-size: 20px;
}
.latestatus{
 display: grid;
 height: auto;
 box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
 padding: 10px 20px 10px 20px;
 border-radius: 30px;
 margin: 10px 10px 10px 10px;
 width: 445px;
 min-height: 130px;
 font-size: 15px;
}
.allStatus{
 display: flex;
}
.latestatus p{
 width: auto;
 position: relative;
}
.copystatus{
 font-weight: 500;
 text-transform: uppercase;
 width: 100px;
 height: 40px;
}
.pagable {
  display: flex;
  flex-direction: column;
  border: var(--pageable-border);
  background: var(--pageable-background);
}

.pagable .pagable-results {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 0.25em;
}

.pagable .pagable-status {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 0.25em;
  background: var(--pageable-status-background);
}

.pagable .pagable-actions {
  display: grid;
  grid-auto-flow: column;
  grid-gap: 0.25em;
}
.pagable .pagable-actions input[name="page-curr"] {
  width: 3em;
}
<html>

<body>
  <div class="mainStatus">
   <h2 class="statusHeading">Latest English Status</h2>
<div class="allquotes"></div>
<div class="pagable-status">
  <label>Page <span class="page-no-curr">1</span> of <span class="page-no-count">1</span></label>
  <div class="pagable-actions">
    <button class="page-btn-first">&#x226A;</button>
    <button class="page-btn-prev">&#60;</button>
    <input type="number" name="page-curr" min="1" value="1" />
    <button class="page-btn-next">&#62;</button>
    <button class="page-btn-last">&#x226B;</button>
    <select name="page-size">
      <option>5</option>
      <option>10</option>
      <option>20</option>
    </select>
  </div>
  <label>(<span class="result-count"></span> items)</label>
</div>

More information about setTimout can be found here: https://www.w3schools.com/jsref/met_win_settimeout.asp

More information about setInterval can be found here: https://www.w3schools.com/jsref/met_win_setinterval.asp