anki icon indicating copy to clipboard operation
anki copied to clipboard

Allow multiple `{{c1::cloze tags}}` in Missing! cards?

Open badlydrawnrob opened this issue 1 year ago • 5 comments

⚠️ This idea might be dead in the water. I don't think it's possible with Anki right now (or ever).

It seems Anki requires you to add a {{c1::cloze tag}} to a cloze field for it to display at all.

No cloze found on card is an error that I've come across. This happens if no {{c1::cloze tag}} is allocated to a cloze field. However, if you have two cloze fields, you don't need to use both! Anki doesn't give an error, if you've added a {{c1::cloze tag}} to at least one field, but complains silently.

  • [x] At least one user of the early version of Anki Themes changed the layout of the cards with more cloze fields.
  • [x] Anki complains LOUDLY if you don't add at least one cloze tag
  • [x] Anki complains silently if your 2nd or 3rd cloze field has no cloze tags.
    • They just don't show up on the card at all 😭
  • [x] You can, however, use a {{c1::cloze tag}} across fields!
    • Anki doesn't seem to render these properly

Making it clear that the field is a cloze type

  • #83

Previous exploration

I think that multiple {{cloze:text}} fields would be problematic, as Anki complains if no cloze tags are assigned:

Basically Anki creates an empty card if you don't add a {{c1:cloze tag}} (see below).

Empty cards with no cloze tags

Here's one card that has a field (the only cloze field) without a card. To delete it, you have to deselect the checkbox keep notes with no valid cards and hit delete. Otherwise the "empty" card will show up in your study deck as in the above images.

Screenshot 2024-01-17 at 16 52 45

badlydrawnrob avatar Jan 17 '24 17:01 badlydrawnrob

Doesn't work

2nd or 3rd cloze field with no tags

  • Both Title and Syntax (inline code) are cloze fields here (without tags)
  • Neither of them render in the card
Screenshot 2024-01-17 at 17 27 15

Also doesn't work ...

A {{c1:cloze tag}} across different fields

  1. Renders the tag (not the answer)
  2. The frontside doesn't even render the field text
    • the reverse renders the full tag (not only the answer)
Screenshot 2024-01-17 at 18 23 39 Screenshot 2024-01-17 at 18 23 46 Screenshot 2024-01-17 at 18 24 02 Screenshot 2024-01-17 at 18 24 09

Does work

2nd, 3rd, 4th, etc cloze field with a tag. However, any cloze field that doesn't have a {{c1::cloze tag}} in it will not render. Damn.

Screenshot 2024-01-17 at 17 27 47

badlydrawnrob avatar Jan 17 '24 18:01 badlydrawnrob

Conditionally load the title if no {{c1::cloze tag}} exists?

Screenshot 2024-01-17 at 18 08 33

badlydrawnrob avatar Jan 17 '24 18:01 badlydrawnrob

@badlydrawnrob I think you reversed the logic in your image.

{{^Text}} will only show the contents if the field is empty. Putting the {{cloze:* Title}} in there would conditionally hide the cloze tag. But the cloze tag is the one that is silently un-rendering, so you'd really want to test on the cloze field and conditionally render the main field.

You'd want to do something like this:

<h4>
  {{cloze:Text}} <-- Cloze will fail to render if Text field contains zero cloze tags.
  {{^cloze:Text}}      <-- if is_empty(Text) 
    {{Text}}             then render Text
  {{/cloze:Text}}          end
</h4>

But regardless, it won't work. The conditionals in the DSM only work with real fields, not the dynamically rendered cloze versions.

Relevant sections of the documentations:

casperlehmann avatar Feb 16 '25 19:02 casperlehmann

I did find a solution to get cloze fields to render, even when they contain zero cloze tags. You'll need to put this Javascript in your card type's frontend template.

Note: My card type has a Text field and a Code field which both may contain cloze tags.

<span class="maybeCloze">
  <h6 class="realCloze">{{cloze:Text}}<h6>
  <h6 class="fakeCloze">{{Text}}</h6>
</span>
<hr>
<pre><code>{{cloze:Code}}</code></pre>

<script>
  maybeClozes = document.getElementsByClassName("maybeCloze");
  for ( i = 0; i < maybeClozes.length; i++ ) {
    maybeCloze = maybeClozes[i]
    real = maybeCloze.getElementsByClassName("realCloze")[0];
    fake = maybeCloze.getElementsByClassName("fakeCloze")[0];
    // Regex to match cloze tags: {{c1::s}}, {{c2::s}}, {{c3::s}}, etc
    if (fake.innerText.match(/\{\{c\d+::.*\}\}/)) {
      real.style.display = "block";
      fake.style.display = "none";
    } else {
      real.style.display = "none";
      fake.style.display = "block";
    }
  }
</script>
  • This approach allows us to target all instances of the maybeCloze class (so we can do this in multiple places in across fields).
  • The code naively assumes that all instances of maybeCloze has one child of each type: realCloze and fakeCloze.
  • Each maybeCloze is handled in isolation, so they don't influence each other.

casperlehmann avatar Feb 16 '25 19:02 casperlehmann

@casperlehmann Hey, thanks for helping out on this! Yes, I understand your code suggestion well enough. The {{^ ...}} should be a "if exists" but perhaps it's not as needed with cloze fields? Last I checked the "No Cloze found on card" was failing pretty hard, erroring when saving the card — how does your javascript suggestion get around that problem? Did you find the way Anki handles it programatically?

If your suggestion works, I feel that's a "great! Nice to have" rather than integrating into the main codebase — I've had cases in the past fail due to updates, and javascript in particular feels a little fragile.

In general Anki has a few problems, and I have some ideas (besides the multiple cloze fields) that probably aren't possible, or preferable to try and shoehorn into Anki.

I've considered creating a coding-focused tool, but I don't know how financially viable that would be. It feels like it'd be pretty cool to have courses and books with a handy flashcard set to revise with, but whether beginners would be ready to pay for that is anyones guess 🤔

badlydrawnrob avatar Feb 17 '25 17:02 badlydrawnrob