New study screen is blocking some of the Javascript features

I am using Ankidroid 2.23.0beta2.

I have found that some JavaScript features are working on desktop Anki and on the standard study screen, but not on the new study screen. As a future development, this can create issues for users when more complicated JavaScript in the template works in the desktop version but not on the Android version.

Example: in one of my cards, I have a prompt for LLM as a field. I want to easily copy it to the clipboard. Below is a card template to achieve this using a JS function assigned to a button. It works on desktop Anki and on the standard study screen, but not on the new study screen it errors out - I introduced `window.prompt` as a fallback.

{{language}}

<hr id=answer>

<div id="content">
  <div id="scenarioTitle">{{scenario_title}}</div>
</div>
<br><br>
<div id="content">
  <div id="scenarioPrompt" style="display: none;">{{scenario_prompt}}</div>
</div>
<br>
<button id="copyBtn" onclick="copyToClipboard()" style="
  padding: 12px 24px;
  font-size: 32px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 8px;
  margin-top: 16px;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
">Copy Scenario Prompt</button>

<script>
  function copyToClipboard() {
    var btn = document.getElementById("copyBtn");
    var originalText = "Copy Scenario Prompt";
    
    var promptDiv = document.getElementById("scenarioPrompt");
    var text = "";
    
    if (promptDiv) {
        var tempDiv = document.createElement("div");
        tempDiv.innerHTML = promptDiv.innerHTML;
        tempDiv.innerHTML = tempDiv.innerHTML
            .replace(/<br\s*\/?>/gi, "\n")
            .replace(/<\/div>/gi, "\n")
            .replace(/<div>/gi, "");
        text = tempDiv.textContent || tempDiv.innerText || "";
        text = text.trim();
    }

    if (!text) return;

    try {
        var textArea = document.createElement("textarea");
        textArea.value = text;
        
        textArea.style.position = "fixed";
        textArea.style.left = "0";
        textArea.style.top = "0";
        textArea.style.opacity = "0";
        textArea.style.width = "1px";
        textArea.style.height = "1px";
        
        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();
        
        try { textArea.setSelectionRange(0, 999999); } catch (e) {}

        var successful = document.execCommand('copy');
        document.body.removeChild(textArea);

        if (successful) {
            showSuccess(btn, originalText);
        } else {
            throw new Error("Auto-copy blocked");
        }

    } catch (err) {
        console.log("Auto-copy failed, showing prompt:", err);
        
        window.prompt("Copy manually:", text);
    }
  }

  function showSuccess(btn, originalText) {
    if (btn.innerText === "Copied!") return;
    var originalColor = "#4CAF50";
    btn.innerText = "Copied!";
    btn.style.backgroundColor = "#45a049";

    setTimeout(function() {
      btn.innerText = originalText;
      btn.style.backgroundColor = originalColor;
    }, 1500);
  }
</script>
1 Like

If someone wants to help, try finding the exact part that it’s failing.

1 Like

I think the issue is in the document.execCommand('copy'); line. The following works in the old study screen but not the new one:

{{Front}}

<button id="copyBtn" onclick="copyToClipboard()">Copy</button>


<script>
function copyToClipboard() {
  var textArea = document.createElement("textarea");
  textArea.value = "hi there";
  document.body.appendChild(textArea);
  textArea.select();
  document.execCommand('copy');
}
</script>
1 Like

If you map the button to call only that after selecting some text manually, it works, so the issue is something else

2 Likes

Nevermind. Figured it out.

3 Likes

For context, the issue doesn’t happen if the focusin and focusout listeners are disabled (source).

I don’t know why they cause the issue, but they do.

They are currently necessary to avoid disabling keypress detection for commands if the webview is focused, which is an improvement over the old reviewer.

By looking at your code, I guess that you could use navigator.clipboard.writeText(text) instead of the deprecated document.execCommand(“copy”), since it doesn’t need to select anything.

Removing the focus listeners is a possibility, but right now I don’t think that the trade-off is worth it, since this use case is rare and actually fixable.

2 Likes

I tried using navigator.clipboard.writeText(text) instead of the document.execCommand(“copy”) in the code, but it stopped working - nothing is getting into the clipboard. Nor for desktop, not on Ankidroid.

1 Like

You should rewrite the code entirely. Something like this works

<button id="copyBtn" onclick="copyToClipboard()">Copy</button>


<script>
function copyToClipboard() {
  navigator.clipboard.writeText(`{{scenario_prompt}}`);
}
</script>

Though it looks like you have to press the button twice for the text to be copied…

2 Likes

I’ve tried this code. Went for reviewing and it’s not working on desktop Anki - even if I click the button 5 times.