Instant type:answer feedback for Cloze Note Type

Is there a way to implement this code, that’s made for basic note type to be used in a cloze note type?

<script>
  (() => {
  /**
   * Type-in-the-answer live feedback for Anki
   * @author Matthias Metelka | @kleinerpirat
   */
    /* Helper functions to keep caret position while typing */
    const getCaretPosition = (contenteditable) => {
      contenteditable.focus();
      let _range = document.getSelection().getRangeAt(0);
      let range = _range.cloneRange();
      range.selectNodeContents(contenteditable);
      range.setEnd(_range.endContainer, _range.endOffset);
      return range.toString().length;
    };
    const setCaretPosition = (contenteditable, pos) => {
      contenteditable.focus();
      document.getSelection().collapse(contenteditable, pos);
    };

    /* Replace input with contenteditable div */
    const input = document.getElementById("typeans");
    const editable = document.createElement("div");
    editable.contentEditable = true;
    editable.id = "typeans";
    editable.innerHTML = input.value;
    input.replaceWith(editable);

    editable.addEventListener("input", () => {
      const pos = getCaretPosition(editable);
      /* Anki uses value attribute to compare answer */
      editable.value = editable.innerText;

      editable.innerHTML = (() => {
        const letters = editable.innerText.split("");
        const word = "{{Answer}}";
        let html = "";
        letters.forEach((letter, i) => {
          html += `<span class="${
            letter == word[i] ? "typeGood" : "typeBad"
          }">${letter}</span>`;
        });
        return html;
      })();
      setCaretPosition(editable, pos);
    });
  })();
</script>
[contenteditable]#typeans {
  border: thin solid var(--border);
  background: var(--frame-bg);
}

Maybe try this. It is a simplified version of the template I’m currently using:

Front Template:

{{cloze:Text}}

<!----------------------------------------------------->

<script>
var currentCloze = document.querySelectorAll(".cloze");

// Hide the original cloze elements and create input boxes
for (i = 0; i < currentCloze.length; i++) {
  currentCloze[i].style.display = "none";
  const input = document.createElement("input");
  input.type = "text";
  input.classList.add("custom_typebox");

  currentCloze[i].insertAdjacentElement("afterend", input);
}

var ctb = document.querySelectorAll('.custom_typebox');

// Iterate through custom_typebox elements to set placeholders and adjust widths
for (j = 0; j < ctb.length; j++) {
  var text = document.getElementsByClassName('cloze')[j].innerText; 
  var result = text.replace("[...]", "");
  var final_result = result.slice(1,-1);
  ctb[j].placeholder = final_result;
  
// Create a hidden element to calculate the actual width of the placeholder
  var hiddenElement = document.createElement('span');
  hiddenElement.innerText = final_result;
  hiddenElement.style.visibility = 'hidden';
  hiddenElement.style.whiteSpace = 'pre'; // Preserve white spaces

// Insert the hidden element into the page and use it to calculate the actual width of the placeholder
  document.body.appendChild(hiddenElement);
  var placeholderWidth = hiddenElement.getBoundingClientRect().width;

// Remove the hidden element from the page
  hiddenElement.parentNode.removeChild(hiddenElement);

// Set the width of the input field
  ctb[j].style.width = placeholderWidth + 9 + "px";
}
</script>

<!----------------------------------------------------->

<script>
   /**
   * Type-in-the-answer live feedback for Anki (vague variant)
   * @author Matthias Metelka | @kleinerpirat
   * Modified by @jcznk (for cloze compatibility, replacements, etc.)
   * Any errors or oversights are to be attributed to me
   * Only works with Anki ~ 2.1.56 and more recent versions.
   */

for (i = 0; i < 999; i++) {
  (() => {
    const input = document.getElementsByClassName('custom_typebox')[i];
    const cloze_content = document.getElementsByClassName('cloze')[i].getAttribute("data-cloze").replace(/<.*?>/g, "").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&'); 
    const answer = cloze_content;

    if (input) {
      input.addEventListener("input", () => {
        input.classList.add("typed");
        input.classList.toggle(
          "goodsofar",
          input.value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") == answer.substring(0, input.value.length).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
        );
        input.classList.toggle(
          "correct",
          input.value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") == answer.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
        );
      });
    }
  })();
}
</script>

Styling section:

/* ------------------------------------------------------ INPUT */

input[type=text]{
  color: black;
  background-color: unset;
  text-align: center;
  cursor: text;
  min-width: 150px;
  width: 150px;
  border-radius: 5px;
  border-style: solid; 
  border-width: 1px;
 }
input[type=text]::placeholder {
  opacity: 0.5;
}
.night_mode input[type=text]::placeholder {
  color: white;
}

/* -------------------------------------------------- TYPECHECK */

.custom_typebox.typed {
  background-color: #ff9999;
}
.custom_typebox.typed.goodsofar {
  background-color: #ffff7f;
}
.custom_typebox.typed.correct {
  background-color: #99ff99;
}
.custom_typebox.typed::placeholder {
  color: black !important;
}

WoW! Thank you! Worked a lot!

*Getting used in the Forum

@jcznk, could you please help me? On the front card, I want to play audio only for c1, c2, and so on, but for some reason, the script returns blank for this TTS code:

{{tts de_DE voices=Apple_Anna_(Premium):cloze-only:Text}}

If I use it on the back card, it plays as expected.

Also, it plays c1 and c2 together. I haven’t figured out how to separate them, so that there’s one play for c1 and another for c2.

Unfortunately, I never used tts in conjunction with cloze deletions, so I have no familiarity with the code.

My first impression, though, is that both your requests are pretty difficult/impossible to accomplish, except maybe by using add-ons or complex workarounds.


On a side note, you might want to add this script to the Front template. I forgot it to include it last time. It allows the use of the Enter key to flip the card, even when the focus is inside a custom typebox.

<script>
for (let i = 0; i < ctb.length; i++) {
    ctb[i].addEventListener("keydown", function(event) {
        if (event.key === 'Enter') {
            if (typeof (pycmd) !== 'undefined') {
                ctb[i].blur(); 
                pycmd('ans'); 
            }
        }
    });
}
</script>

Amazing all your help!

I found something that worked as well.

 [anki:tts lang=de_DE voices=Apple_Anna_(Premium)] {{Artikel}}{{Wort_DE}}[/anki:tts]

I’ve been trying all day to convert the cloze script into a type-in-the-answer format, but unfortunately, I haven’t had any success.

By the way…do you have any type-in-the-answer card version of it?

I’m not sure if I’ve understood what you’re trying to achieve. Would you please elaborate your request?

I achieved with this:

<div class="class">
    <input type="text" class="ctb" placeholder="Type your answer here..." id="typeans">
    <!-- Assuming there's a replay button or other elements -->
</div>

<script>
  (() => {
    const input = document.getElementById("typeans");
    const answer = "{{Wort_DE}}"; // Replace "CorrectAnswer" with your actual answer string

    if (input) {
      input.addEventListener("input", () => {
        input.classList.add("typed");
        input.classList.toggle(
          "goodsofar",
          input.value == answer.substring(0, input.value.length)
        );
        input.classList.toggle(
          "correct",
          input.value == answer
        );
      });
    }
  })();
</script>

I wanted a input text box that I could use on phone and mac, with all the type-in-text feedback.

The “Type-in-the-answer live feedback” script I posted does not work on 2.1.54. Some of the code it interacts with (mainly, the data-cloze attribute) was only introduced around version ~ 2.1.56.

EDIT: added a comment about this to the script.

Yeh I’ve figured out so I’ve deleted my message, but you’ve been pretty fast on answer my stupid question. Thank you so much!

It’s work seamlessly, the only thing I could figure it out is the @a_ps last code, with the answer box, I mean the script work pretty well just like this so it’s not a real problem.