1

I’m a beginner, so I apologize if I’m missing something obvious.

I’m trying to write a simple web app. When you type text into a textarea and click a button, a “card” (div) is created with that textarea’s value as its innerText. When you click on a card, its innerText is then copied to the clipboard.

It works when I write out the function this way:

el.setAttribute(
      "onclick",
      "console.log(navigator.clipboard.writeText(this.innerText));"
    );

But if I write out the function separately and call it when setting the attribute, undefined is copied to the keyboard:

el.setAttribute("onclick", "copyText()");

I’d stick with the first, working option, except for two things:

  1. Primarily I’m just trying to learn, so avoiding difficulties because I don’t understand them doesn’t really make sense.
  2. I want to add more lines of code to the copyText() function so that it works on mobile devices, too. Can I even do that the first way?

Here’s my code in full:

const app = document.getElementById("app");

function createCard() {
  let input = document.getElementById("textarea").value;
  if (input == "") {
    console.log("You must enter text to create a card.");
  } else {
    const el = document.createElement("div");
    el.innerText = document.getElementById("textarea").value;
    el.setAttribute("class", "item card");
    el.setAttribute("onclick", "copyText()");
    app.appendChild(el);
    document.getElementById("textarea").value = "";
  }
}

function copyText() {
  navigator.clipboard.writeText(this.innerText);
}

I expected it to work exactly the same as the following, but it doesn’t. It returns undefined.

const app = document.getElementById("app");

function createCard() {
  let input = document.getElementById("textarea").value;
  console.log(input);
  if (input == "") {
    console.log("You must enter text to create a card.");
  } else {
    const el = document.createElement("div");
    el.setAttribute("class", "item card");
    el.setAttribute(
      "onclick",
      "console.log(navigator.clipboard.writeText(this.innerText));"
    );
    el.innerText = document.getElementById("textarea").value;
    app.appendChild(el);
    document.getElementById("textarea").value = "";
  }
}

I suspect it’s an issue with “this” and scope, but I can’t figure it out. Sorry again—I know this is very much a beginner’s question. Thanks for your help.

  • To set event handlers, it's more widely accepted to use `addEventListener()` rather than `setAttribute()`. But as for your particular problem, you're right, `this` doesn't exist in the context it's being used. The simple solution is to say `navigator.clipboard.writeText(document.getElementById("textarea").value)`. – kmoser Jan 03 '23 at 17:01
  • Thanks, @kmoser. It seems like it would make sense to cut out the middleman and get the text directly from the textarea—but my idea is to have multiple cards on the screen at once, and when I want to copy and paste the text from one, I would click on it to copy its contents. If I used `navigator.clipboard.writeText(document.getElementById("textarea").value)` it would copy what’s currently in the textarea (unless I’m mistaken). – Erick Piller Jan 03 '23 at 18:51

1 Answers1

1

Calling copyText() calls it without a this context. You would need to use

el.setAttribute("onclick", "copyText.call(this)");
// ideally also pass the event object:
el.setAttribute("onclick", "copyText.call(this, event)");

However, the best practice is to install an event handler function instead of using the onclick attribute, so you should do

el.onclick = copyText;
// or
el.addEventListener("click", copyText);

Notice these refer to the copyText function that is in scope, it doesn't need to be a global variable.

const input = document.getElementById("textarea");
const app = document.getElementById("app");
function createCard() {
  console.log(input.value);
  if (!input.value) {
    console.log("You must enter text to create a card.");
  } else {
    const el = document.createElement("div");
    el.className = "item card";
    el.onclick = function() {
      console.log(navigator.clipboard.writeText(this.textContent));
    };
    el.textContent = input.value;
    app.appendChild(el);
    input.value = "";
  }
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375