How to target current cloze only? (Javascript)

Hello,
I would like to create a button which, when clicked, reveals the first character of the current cloze, e.g.:
If my note is: “{{c1::This}} {{c2::is}} {{c3::an}} {{c4::example}}” and card n°4 is currently displayed, clicking the button would show the letter “E”

I have no clue, though, on how to target the current cloze only.
Is it possible? Any ideas?

Thank you in advance

Here is an example.

{{cloze:Text}}
<div id="raw-text" hidden>{{Text}}</div>
<div><button onclick="showFirstLetter();">Show First Letter</button></div>
<div>First Letter: <span id="first-letter"></span></div>

<script>
    var currentClozeNumber = {{#c1}}1{{/c1}}{{#c2}}2{{/c2}}{{#c3}}3{{/c3}}{{#c4}}4{{/c4}}{{#c5}}5{{/c5}};

    function getCurrentClozedText(num) {
        const re = new RegExp(`({)({)c${num}::(?<clozedText>.*?)(::.*?)*(})(})`, "s");
        const rawText = document.getElementById("raw-text").innerHTML;
        const match = re.exec(rawText);
        return match.groups.clozedText;
    }

    function showFirstLetter() {
        document.getElementById("first-letter").textContent =
            getCurrentClozedText(currentClozeNumber).trim()[0];
    }
</script>

Tested on Anki desktop version 2.1.50 beta8 and AnkiDroid. Not sure if it works on AnkiMobile.

3 Likes

Very nice! It seems to work on Anki 2.1.49 too. Thank you :pray:

One last thing: do you have an idea on how to make the code work for all the clozes, if there are 2+ clozes with the same number? Right now it works on the first one only.
e.g. if note is “{{c1::This}} {{c1::is}} {{c2::an example}}” and card n°1 is now displayed, if I click on the button, the “T” will be displayed but not the “i”

(I’m using a modified version of your code, which, instead of displaying just the first letter, displays each letter sequentially; these are the lines I added/modified:
var nextChar = 0
getCurrentClozedText(currentClozeNumber).trim()[0, nextChar++]; )

Try adding the global flag to RegExp and using matchAll(), like this:

{{cloze:Text}}
<div id="raw-text" hidden>{{Text}}</div>
<div><button onclick="revealNextChar();">Reveal Next Char</button></div>
<div>Hint:</div>
<div id="cloze-hint"></div>

<script>
    var currentClozeNumber = {{#c1}}1{{/c1}}{{#c2}}2{{/c2}}{{#c3}}3{{/c3}}{{#c4}}4{{/c4}}{{#c5}}5{{/c5}};
    var endIndex = 0;
    var hintContainer = document.getElementById("cloze-hint");

    function getClozedWords(num) {
        const re = new RegExp(`({)({)c${num}::(?<clozedWord>.*?)(::.*?)*(})(})`, "gs");
        const rawText = document.getElementById("raw-text").innerHTML;
        return [...rawText.matchAll(re)].map((match) => match.groups.clozedWord.trim());
    }

    function revealNextChar() {
        endIndex++;
        hintContainer.innerHTML = "";
        getClozedWords(currentClozeNumber).forEach((word) => {
            const div = document.createElement("div");
            div.textContent = word.slice(0, endIndex);
            hintContainer.appendChild(div);
        });
    }
</script>
4 Likes

It seems to works perfectly! Thank you a lot!

1 Like

It works very well, it would be interesting if we could add a shortcut to reveal the characters.