Introduction
Anki is brilliant. It has really lived up to its motto ‘Remember Anything, Remember Anywhere, Remember Efficiently’ for being my go-to way to remember notes and other information. This is made all the more powerful through its HTML-based card system, which includes support for complex behaviour via JavaScript. Unfortunately, utilising JavaScript to perform such behaviour is less than straightforward.
Fortunately, I've done all the hard work for you, so here's a guide on using JavaScript in Anki. I used it to generate physics questions and associated solutions, but the general idea should be applicable to any use case.
Getting JavaScript into Anki
Although the ‘Add Note’ screen in Anki allows HTML and JavaScript input, any JavaScript input here is automatically executed during the editing stage, which is not exactly helpful for creating dynamic content.
Instead, JavaScript needs to be set up from within the card template. To do this, create a new note type by accessing the
Add Note screen, then clicking the note type (
Basic, in the images above),
Manage,
Add,
Add: Basic,
OK.
Give it a name like ‘Script’, then go back to the
Choose Note Type window and choose the new note type.
Click the
Cards… button, and at the beginning of the
Front Template, add some JavaScript. If all goes well, the
Front Preview should show the results of the JavaScript code.
Custom Per-Card JavaScript
Now creating a new note type for every single piece of JavaScript you want to add is impractical, so let's find a way to make the note type execute code based on a per-card field. At the beginning of the
Front Template, replace the previous JavaScript code with:
var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending comments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); //Replace special symbols.
eval(code);
The first two lines take the HTML code of a per-card
Script field and store it in a JavaScript variable. The third and fourth lines strip extraneous HTML code from the variable, hopefully turning it into executable JavaScript code. Depending on which symbols you use, you may need to add more replacements to the fourth line. The final line executes the JavaScript code.
Right now, the code won't work, because the
Script field has not been declared. Close the
Card Types for Script window, click the
Fields… button, and add a new
Script field.
Now, any code in the Script field will be executed each time the card is shown, once for the front, then again later for the back.
In order to access the contents of the card more easily from within JavaScript, some changes can be made to the
Front Template and
Back Template, giving each side an id attribute:
Note that if you want to access these, you will need to delay the call until after the DOM has finished loading, by wrapping your JavaScript in
window.setTimeout(function(){ …
}, 0); or something similar.
Data Persistence
Depending on what you want to do, the above technique will work perfectly. More likely, however, is that the fact the JavaScript is executed twice will cause problems for you. The images below show a common scenario, wherein the generated question changes when flipping sides of the card. Not useful for checking your answer!
Thus, we need a way of storing data within the JavaScript code across executions of the same card. Unfortunately, typical ways of doing this,
localStorage and
document.cookie are disabled. From a ‘normal’ JavaScript point of view, each execution is entirely separate from the other.
Thankfully, Anki exposes a behind-the-scenes
window.py object. The internal functions exposed by the object are pretty useless, but the instance of the object is shared between executions of the same card (and reset if the card is shown again later), meaning that it can be used to store data between executions.
Using the below code to store randomly generated data between executions allows us to overcome this restriction.
window.setTimeout(function() {
var o = (typeof py === "undefined") ? {} : py; //Attempt to use Anki's Bridge object to store data across sides.
o.data = o.data || {};
o.data.num = o.data.num || Math.random();
if (document.getElementById("front")) document.getElementById("front").innerHTML = "The number on the front is " + o.data.num;
if (document.getElementById("back")) document.getElementById("back").innerHTML = "The number on the back is " + o.data.num;
}, 0); //Execute after Anki has loaded its Bridge object.
Anki+JavaScript in Action