AnkiMobile one-by-one cloze reveal

Hello!
I’m trying to be able to create cloze card with progressive reveal, i.e.

SQL commands can be divided into:
{{c1:: Data Definition Language }}
{{c1:: Data Manipulation Language }}
{{c1:: Data Control Language }}

So that clozes are revealed sequentially.
Following template provides this functionality in exactly the form I’m looking for https: //ankiweb .net/shared/info/1874787050
Front:

<script>
var logDiv = null;
function log(s) {
  if (logDiv == null) {
    logDiv = document.createElement("div");
    logDiv.id = 'log_debug';
    logDiv.style = 'position:absolute;left:0;top:0;z-index:-2;color:grey;font-size:small';
    document.body.append(logDiv);
  }
  logDiv.insertAdjacentHTML('beforeend', s + '<br/>');
}
/*
function logSizes() {
  let ovl = document.getElementById('tap_overlay');
  let rect = ovl.getBoundingClientRect();
  let vp = window.visualViewport;
  log(`ovl: ${rect.width.toFixed(2)},${rect.height.toFixed(2)} vp: ${vp.width.toFixed(2)},${vp.height.toFixed(2)}`);
}
*/


function getCardNumber() {
  clz = document.body.className;
  const regex = /card(\d+)/gm;
  let m;

  if ((m = regex.exec(clz)) !== null) {
    return m[1];
  } else {
    console.error("Cannot find cardN class of body element!");
    return "0";
  }
}

function getClozes(str, cardNumber) {
  const regex = new RegExp(`\{\{c${cardNumber}::(.*?)(\}\}|::.*?\}\})`, 'gm')
  //console.log(regex);
    let m;
  const clozes = [];
    while ((m = regex.exec(str)) !== null) {
        // This is necessary to avoid infinite loops with zero-width matches
        if (m.index === regex.lastIndex) {
            regex.lastIndex++;
        }
        m.forEach((match, groupIndex) => {
            //console.log(`Found match, group ${groupIndex}: ${match}`);
     if (groupIndex == 1) {
                clozes.push(match);
            }
        });
    }
  return clozes;
}

function clickHandler(e){
   //console.log(`${e.target.tagName}(${e.target.id})`);
   const tt = e.target
   if ( tt instanceof HTMLElement &&
       ( tt.id === 'qa' || 
         tt.tagName === 'HTML' ||
         tt.tagName === 'LI' ||
         tt.tagName === 'I' )) {
     //log('reveal');
     revealNextCloze();
   }
}


var elements;
var clozes;
var revealed = [];

function revealCloze(i) {
  if (!revealed[i]) {
    elements[i].innerHTML = clozes[i];
    revealed[i] = true;
  }
}

function revealNextCloze() {
  firstUnrevealed = revealed.findIndex ( el => !el );
  //log(firstUnrevealed);
  if (firstUnrevealed != -1) {
    revealCloze(firstUnrevealed);
  } 
}

onUpdateHook.push(function() {
  //console.log(`inside update hook`);

    var text = document.getElementById("rawText").innerHTML ;
    //console.log(text);
    clozes = getClozes(text, getCardNumber());
  //console.log(clozes);
    
    elements = document.querySelectorAll(".cloze");

  if (clozes.length != elements.length) {
    console.error("Inconsistent cound of clozes found in original note text and in the card!");
    return;
  }
  elements.forEach((el, i) => {
    el.addEventListener('click', e => {
      revealCloze(i);
      //log(i);
    })
  });
  revealed.length = elements.length;
  revealed.fill(false);
  
  window.addEventListener('click', clickHandler);

});

</script>
<script id="rawText" type="text/plain">
{{Text}}
</script>
{{cloze:Text}}

Back:

<script>
  window.removeEventListener('click', clickHandler);
</script>
{{cloze:Text}}<br>
{{Extra}}

It works on Windows and Android, however, it doesn’t work on iOS - tapping reveals all clozes at once. In config, all the taps are set to default - “Show answer”.
I understand that I need to add tappable to the class (https:// docs .ankimobile.net/more.html#javascript), so far I’ve tried the following with no luck:

<script>
function revealCloze(evt) {
    const clozeEl = evt.target.closest(".cloze");
    if (clozeEl && !clozeEl.dataset.revealed) {
        const clozeContent = clozes[clozeEl.dataset.index];
        clozeEl.innerHTML = clozeContent;
        clozeEl.dataset.revealed = "true";
    }
}

function getCardNumber() {
    const clz = document.body.className;
    const match = clz.match(/card(\d+)/);
    return match ? match[1] : "0";
}

function getClozes(str, cardNumber) {
    const regex = new RegExp(`\{\{c${cardNumber}::(.*?)(\}\}|::.*?\}\})`, 'gm');
    const clozes = [];
    let match;
    while ((match = regex.exec(str)) !== null) {
        if (match[1]) {
            clozes.push(match[1]);
        }
    }
    return clozes;
}

var clozes;

onUpdateHook.push(function() {
    const text = document.getElementById("rawText").innerHTML;
    clozes = getClozes(text, getCardNumber());
    
    const elements = document.querySelectorAll(".cloze");
    elements.forEach((el, index) => {
        el.dataset.index = index;
        el.addEventListener("click", revealCloze);
        el.classList.add('tappable');
    });
});
</script>
<script id="rawText" type="text/plain">
{{Text}}
</script>
{{cloze:Text}}

Need help adjusting this for iOS.

1 Like

Please don’t be rude when folks post asking for help. If you don’t want to help someone, there’s no need to respond. FAQ - Anki Forums

1 Like

If this were the case, maybe one of the most popular user cases for Anki in general - Anking (https ://www. theanking. com/) - wouldn’t have to implement (https ://ankiweb .net/ shared/info/952691989) the same one-by-one cloze reveal - albeit in more cumbersome way in case of iOS.

ok, i dont see this is iOS specific.
i use ankidroid and there are solutions.
i better not comment as i dont run anki on iOS.
thanks

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.