[Suggestion] Add {{#OnDesktop}}{{/OnDesktop}}

Anki offers the option to use {{#FieldName}}{{/FieldName}} to conditionally execute pieces of code, depending on wether such field is empty or not.

This is very useful, as it grants the user a lot of power over the way they want their notes and cards to behave, even allowing to add a condition for the execution of javascript code.

However, as far as I know, no such option exists to decide wether you want some piece of code to only be executed on AnkiDestop, AnkiMobile/AnkiDroid, etc.
I know of .android .win .mac and the like, but, as far as I understand, these classes are mostly useful for styling purposes.

I think adding such option would make a lot of sense:

  • speaking in general, it seems like a fairly simple change which would allow the user to have a lot more control over their notes and cards
  • it could be employed to prevent the load of long, platform specific code, e.g. HTML and javascript that require an addon to be useful
    (as they serve no purpose on mobile, all they do is to negatively affect the performance of the app, sometimes quite remarkably)
2 Likes

What if you wrap the entire front of your card with {{#OnDesktop}}{{/OnDesktop}}? When the card has empty front, Anki doesn’t generate it. Desktop Anki would generate such a card, but what should AnkiDroid do with it?
image

1 Like

Good question! I was also wondering.

I would say that {{#OnDesktop}}{{/OnDesktop}}:

  • should allow for the conditional execution of code
  • it should not allow for the conditional creation of cards.

Anki already behaves like this for {{#c1}}{{/c1}}, {{#c2}}{{/c2}}, {{#c3}}{{/c3}}, etc. in cloze notes.

So if you wrap the entire Front/Back of the card with {{#OnDesktop}}{{/OnDesktop}}, when you open it from mobile its Front/Back templates’ code will not be loaded, but the card won’t count as empty.

I feel like we shouldn’t tempt users to overuse conditional replacements for scripts. You could just assign a boolean for each client to globalThis:

var conditions = {
    AnkiDesktop: document.documentElement.className.split(/\s/).some(c => /linux|win|mac/.test(c)),
    AnkiWeb: /AnkiWeb/.test(document.querySelector("title").innerText),
    AnkiMobile: document.documentElement.classList.contains("safari"),
    AnkiDroid: globalThis.AnkiDroidJS != null,
}

Object.keys(conditions).forEach((key) => {
    globalThis[`is${key}`] = conditions[key];
});

… and then wrap your function calls with if blocks:

if (globalThis.isAnkiDesktop) {
  doSomething();
}
This particular set of conditions probably doesn’t allow separating between AnkiWeb and AnkiMobile on iOS.

Why?

In the past I also loved the conditional replacement feature and used it extensively in my templates (not just to wrap entire <script> tags, but for individual function calls). But there came a time when my template got too complex to code inside Anki, so I had to migrate to an IDE. From that point on I found the {{FieldName}} syntax to be a hindrance, as VS Code doesn’t understand it and marks it as invalid JS.

My point is, we should not incentivise people to use syntax that’s exclusive to Anki within their JavaScript when there are ways to achieve the same result without it.

3 Likes

To distinguish between AnkiMobile and AnkiWeb (for those who review on safari), you could simply test for safari and not ankiweb, ie document.documentElement.classList.contains("safari") && !/AnkiWeb/.test(document.querySelector("title").innerText).

3 Likes

On one hand I do agree, but I also would say:

  • {{#FieldName}}{{/FieldName}} does not only serve to prevent JavaScript from being executed, but also HTML (e.g. buttons, divs, audio files), all in one go.
  • While maybe less rigorous, conditional replacements seem to be much more user friendly.
    (you just have to wrap everything once and with very short and simple code, instead of setting up variables, adding if blocks, maybe also functions to deal with HTML etc.)

Maybe the best thing would be to have some kind of unified way to access this information through JavaScript, instead of relying on side effects of being on a certain platform or not to distinguish the cases, ie. some kind of global variable could be set indicating where you are.

That way, it would still be relatively straightforward to distinguish cases while still being IDE-friendly (even though you’d still have to convince it a variable is indeed already defined — but that’s always less painful than interfering with JS’ syntax).

1 Like

This is very helpful and it solves my problem about mp4 files:
ankidesktop doesn’t support mp4 in video tag. so I have to use sound
but in ankidroid, sound doesn’t show a control, forcing me to use video.

There is a minor issue in AnkiDesktop condition:
The code below check if there is “linux, win or mac” in className.

AnkiDesktop: document.documentElement.className.split(/\s/).some(c => /linux|win|mac/.test(c))

But in my Ankidroid, the ClassName is something like “mobile linux android JS”, so isAnkiDestop will be labeled incorrectly as True.

Since I only use desktop in win, I took the simple workaround to just delete linux in the condition

AnkiDesktop: document.documentElement.className.split(/\s/).some(c => /win|mac/.test(c)),

if you also want to use linux, maybe you can do :

“document.documentElement.className.split(/\s/).some(c => /linux|win|mac/.test(c)) &&
!globalThis.AnkiDroidJS”

Greetings, Yuan.
I am so happy someone has solved this problem. I have been struggling with mp4 in video tag for 2 days.
Unfortunately, I am very bad at coding and my knowledge of JavaScript is null.
This is what I have so far:

<script>
var conditions = {
    AnkiDesktop: document.documentElement.className.split(/\s/).some(c => /linux|win|mac/.test(c)),
    AnkiWeb: /AnkiWeb/.test(document.querySelector("title").innerText),
    AnkiMobile: document.documentElement.classList.contains("safari"),
    AnkiDroid: globalThis.AnkiDroidJS != null,
}

Object.keys(conditions).forEach((key) => {
    globalThis[`is${key}`] = conditions[key];
});

if (globalThis.isAnkiDesktop) {
  doSomething({{Video1}});
}
if (globalThis.isAnkiDroid) {
  doSomething({{<video width="400" height="350" controls autoplay>
<source src="{{Video2}}" type="video/mp4">   
</video>}});
}
</script>

Is there any chance you could help me how to make this code work?