Is it even possible to persist javascript variables on ankidroid?

I have checked into a few posts about this topic, and after successfully creating some random fields with a suggestion from @jcznk and now confronted with the issue of getting those fields on the card back. Works fine in Anki desktop but not Ankidroid.

After a ton of websearching, I have tried using global variables, assigning directly to the window element, assigning to the card element, and also the Anki Persistence script.

the variable, window.variable, card.variable all work on Desktop. No problem there. I did post this question on the Ankdroid github issues, and got a suggestion to try this storage.ts script. I didn’t yet attempt to complie this to js, but it also led me to try (unsuccessfully) js.storage.js, and another storage.js as well.

As I now understand, Ankidroid refreshes the webview between front and back cards. I also have the feeling that all these solutions above, persistence, storage.ts, storage.js and including html5 storage api, all use the window element to set/get the variable. (Is that correct?)

Is there any other way? Or is this just not possible on Ankidroid?

It is true that Web Storage API, such as sessionStorage, cannot be used to persist variables, but it is possible to store key/value pairs (both strings only) in sessionStorage on the question side, and then retrieve those values on the answer side. Anki-Persistence uses sessionStorage internally for clients like AnkiDroid, where global variables are not persisted.

Suppose you want to keep data between the question and answer on both Anki Desktop and AnkiDroid:

  • If you are using Anki 2.1.49 or earlier, I recommend using Anki-persistence.
  • If you are using Anki 2.1.50 or later, you can simply use sessionStorage, as sessionStorage is also available on Anki desktop.

Here is an example of using sessionStorage to display 3 random fields from 10 fields for both question and answer. ( This script does not work with 2.1.49 or earlier)

Front Template
{{Front}}
<hr>

<div id="fields-output"></div>

<div class="field" hidden>{{Field 01}}</div>
<div class="field" hidden>{{Field 02}}</div>
<div class="field" hidden>{{Field 03}}</div>
<div class="field" hidden>{{Field 04}}</div>
<div class="field" hidden>{{Field 05}}</div>
<div class="field" hidden>{{Field 06}}</div>
<div class="field" hidden>{{Field 07}}</div>
<div class="field" hidden>{{Field 08}}</div>
<div class="field" hidden>{{Field 09}}</div>
<div class="field" hidden>{{Field 10}}</div>

<script>
{
    const fieldCount = 3;
    const fieldsContainer = document.getElementById("fields-output");
    const fields = [...document.querySelectorAll(".field")];
    // Durstenfeld shuffle algorithm
    // https://stackoverflow.com/a/12646864
    function shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
    }
    const availableFieldIndexes = fields.reduce((prev, current, index) => {
        if (current.textContent.trim()) {
            prev.push(index);
        }
        return prev;
    }, []);
    shuffleArray(availableFieldIndexes);
    const randomIndexes = availableFieldIndexes.slice(0, fieldCount);
    sessionStorage.setItem("randomIndexes", JSON.stringify(randomIndexes));
    for (const index of randomIndexes) {
        fieldsContainer.appendChild(fields[index]);
        fields[index].hidden = false;
    }
}
</script>
Back Template
{{Front}}
<hr>
{{Back}}
<hr>

<div id="fields-output"></div>

<div class="field" hidden>{{Field 01}}</div>
<div class="field" hidden>{{Field 02}}</div>
<div class="field" hidden>{{Field 03}}</div>
<div class="field" hidden>{{Field 04}}</div>
<div class="field" hidden>{{Field 05}}</div>
<div class="field" hidden>{{Field 06}}</div>
<div class="field" hidden>{{Field 07}}</div>
<div class="field" hidden>{{Field 08}}</div>
<div class="field" hidden>{{Field 09}}</div>
<div class="field" hidden>{{Field 10}}</div>

<script>
{
    const fieldCount = 3;
    const fieldsContainer = document.getElementById("fields-output");
    const fields = [...document.querySelectorAll(".field")];
    function shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
    }
    let randomIndexes;
    if (sessionStorage.getItem("randomIndexes")) {
        randomIndexes = JSON.parse(sessionStorage.getItem("randomIndexes"));
        sessionStorage.clear();
    }
    else {
        // Previewer(if Back Side Only) and Card Layout Editor(Back Template)
        const availableFieldIndexes = fields.reduce((prev, current, index) => {
            if (current.textContent.trim()) {
                prev.push(index);
            }
            return prev;
        }, []);
        shuffleArray(availableFieldIndexes);
        randomIndexes = availableFieldIndexes.slice(0, fieldCount);
    }
    for (const index of randomIndexes) {
        fieldsContainer.appendChild(fields[index]);
        fields[index].hidden = false;
    }
}
</script>

Random Fields (for 2.1.50+ and AnkiDroid).apkg

5 Likes

Presumably that’s only the case for the -qt6 build, at least on Mac which is still using Qt 5.14?

ok Thanks so much for the insight here. Actually part of my problem then was the anki persistence script on Anki 2.51. But now I have reverted to 2.49 with a crowdanki issue. So will just wait a bit till thats fixed until implementing the above. I don’t want to have something that will prevent me from updating Anki in the future. Thanks for the shuffle algorithm! I knew this would be possible, but just not there yet. I had 3 while loops that worked, but this is clearly the right way!

1 Like

No, it appears not. I have been able to use sessionStorage since version 2.1.50 on Windows and Linux, even with Qt5 builds. I don’t have a Mac, so I can’t say for sure, but I would guess that it probably has nothing to do with whether the Qt version is 5 or 6. I assume that the change is probably due to the fact that the page url is now set by this commit:

4 Likes

Is it intended that now localStorage also works?
Because Anki version 2.1.49 and earlier have not this so that I have to use anki-persistence.

image
Version ⁨2.1.50 (26d40c3a)⁩
Python 3.9.7 Qt 6.2.2 PyQt 6.2.2

1 Like

Presumably @hkr’s explanation applies there too.

1 Like

Solution working awesome!! 2.1.52 qt5. Thanks!!

1 Like