An Anki note type with 5 audios and 5 transcriptions for a single word/phrase, from which one is selected randomly whenever the card is shown. The card has only a front side with the audio, and a collapsible section with the audio's transcription and more info (e.g., definitions, image, etc).
- If you fail the card, next time Anki shows it all the audios and transcriptions are shown instead of a single audio and a single transcription.
Word
IPA
Definitions
Audio1
Transcription1
Audio2
Transcription2
Audio3
Transcription3
Audio4
Transcription4
Audio5
Transcription5
<div id="root">
<div id="inner-card">
<div id="audio-container" class="audio">
<!--
NOTE: We must specify the fields because Anki needs to render them into the HTML
first before JS can manipulate it
-->
{{#Audio1}} <span id="audio1">{{Audio1}}</span> {{/Audio1}}
{{#Audio2}} <span id="audio2">{{Audio2}}</span> {{/Audio2}}
{{#Audio3}} <span id="audio3">{{Audio3}}</span> {{/Audio3}}
{{#Audio4}} <span id="audio4">{{Audio4}}</span> {{/Audio4}}
{{#Audio5}} <span id="audio5">{{Audio5}}</span> {{/Audio5}}
</div>
<div id="details-container">
<details>
<summary><i>Test yourself!</i></summary>
<div id="transcription-container" class="sentence">
<!--
NOTE: We must specify the fields because Anki needs to render them into the HTML
first before JS can manipulate it
-->
{{#Transcription1}} <span id="transcription1">{{Transcription1}}</span> {{/Transcription1}}
{{#Transcription2}} <span id="transcription2">{{Transcription2}}</span> {{/Transcription2}}
{{#Transcription3}} <span id="transcription3">{{Transcription3}}</span> {{/Transcription3}}
{{#Transcription4}} <span id="transcription4">{{Transcription4}}</span> {{/Transcription4}}
{{#Transcription5}} <span id="transcription5">{{Transcription5}}</span> {{/Transcription5}}
</div>
<hr>
<span class="entry">
{{#Word}}
<span class="word">
<span class="HYPHENATION"> {{Word}} </span>
{{#IPA}}
<span class="PronCodes">
<span class="neutral span"> /</span>
<span class="PRON"> {{IPA}} </span>
<span class="neutral span">/</span>
</span>
{{/IPA}}
</span>
{{/Word}}
{{#Definitions}}
<div class="definitions"> {{Definitions}} </div>
{{/Definitions}}
</span>
{{#Image}}
<div class="image"> {{Image}} </div>
{{/Image}}
</details>
</div>
</div>
</div>
<script>
// These IDs corresponds to the declared elements above. Whenever you add more fields,
// you must add them here as well. Otherwise, they won't participate in
// the random selection.
const AUDIO_IDS = ["audio1", "audio2", "audio3", "audio4", "audio5" ];
const TRANSCRIPTION_IDS = ["transcription1", "transcription2", "transcription3", "transcription4", "transcription5" ];
const root = document.getElementById("root");
const rootChildren = root.children;
const innerCard = rootChildren[0];
pickRandomAudioWithSentence(AUDIO_IDS, TRANSCRIPTION_IDS);
/*
* Functionalities.
*/
/*
* Pick random audio from fields (and corresponding transcription) and display it with remaining fields.
*/
function pickRandomAudioWithSentence(audioIds, transcriptionIds) {
// Get all non-empty audio elements.
const nonEmptyAudioElems = audioIds
.map(audioId => document.getElementById(audioId))
.filter(element => !!element && element.hasChildNodes());
// Get a random non-empty audio element.
const elemIndex = getRandomInt(nonEmptyAudioElems.length);
const audioElem = nonEmptyAudioElems[elemIndex];
// Get all non-empty transcription elements.
const nonEmptyTranscriptionElems = transcriptionIds
.map(transcriptionId => document.getElementById(transcriptionId))
.filter(elem => elem !== null);
// Get the first transcription element whose ID ends with the same ID as
// the selected audio element.
const transcriptionElem = nonEmptyTranscriptionElems.find(elem => elem.id.endsWith(audioElem.id.slice(-1)));
// If we got an audio element, then update the audio container with it only.
if (audioElem) {
let audioContainer = document.getElementById("audio-container");
audioContainer = removeAllChildNodes(audioContainer);
audioContainer.appendChild(audioElem);
}
// If we got an audio element, then update the audio container with it only.
if (transcriptionElem) {
let transcriptionContainer = document.getElementById("transcription-container");
transcriptionContainer = removeAllChildNodes(transcriptionContainer);
transcriptionContainer.appendChild(transcriptionElem);
}
}
function getRandomInt(max) { return Math.floor(Math.random() * max); }
/*
* Remove all child nodes from an HTML element.
*
* NOTE: `replaceChildren` is the fully supported and faster way of doing this
* but I couldn't get it to work.
* See https://stackoverflow.com/a/3955238
*/
function removeAllChildNodes(element) {
while (element.firstChild) {
element.removeChild(element.lastChild);
}
return element;
}
</script>
Empty
:root {
/** CSS DARK THEME PRIMARY COLORS */
--color-primary-100: #2196f3;
--color-primary-200: #50a1f5;
--color-primary-300: #6eacf6;
--color-primary-400: #87b8f8;
--color-primary-500: #9dc3f9;
--color-primary-600: #b2cffb;
/** CSS DARK THEME SURFACE COLORS */
--color-surface-100: #121212;
--color-surface-200: #282828;
--color-surface-300: #3f3f3f;
--color-surface-400: #575757;
--color-surface-500: #717171;
--color-surface-600: #8b8b8b;
--color-surface-800: #F2F1EB;
/** CSS DARK THEME MIXED SURFACE COLORS */
--color-surface-mixed-100: #2a4f79;
--color-surface-mixed-200: #436187;
--color-surface-mixed-300: #5b7396;
--color-surface-mixed-400: #7285a4;
--color-surface-mixed-500: #8999b3;
--color-white: #FFFFFF;
--color-silver-white: #F2F2F2;
}
.card {
font-family: arial;
font-size: 20px;
text-align: center;
color: var(--color-surface-200);
background-color: white;
}
.nightMode .card {
background-color: var(--color-surface-100);
}
#inner-card {
padding: 1em;
border-radius: 10px;
background-color: var(--color-silver-white);
}
.nightMode #inner-card {
color: var(--off-white);
background-color: var(--color-surface-200);
}
b {
color: var(--color-primary-100);
text-decoration: underline;
font-style: italic;
}
.nightMode b {
color: var(--color-primary-400);
}
.sentence {
font-size: 1em;
font-style: italic;
margin-top: 2em;
margin-bottom: 2em;
}
.word {
font-size: 30px;
font-weight: bold;
margin-bottom: 1em;
color: var(--color-primary-100);
}
.definition {
color: var(--color-surface-200);
margin-bottom: 1em;
}
.nightMode .definition {
color: var(--color-white);
}
.translation {
font-size: 0.8em;
font-weight: 100;
margin-bottom: 1em;
color: var(--color-surface-100);
}
.nightMode .translation {
color: var(--color-surface-800);
}
.image {
display: inline-block;
}
img {
border-radius: 15px;
width: 75%;
}
hr {
border: 1px dashed grey;
background-color: white;
}
.nightMode hr {
border: 1px dashed var(--color-surface-400);
background-color: var(--color-surface-200);
}
#details-container {
margin-top: 2em;
}
#audio-container {
text-align: center;
}
.entry .word {
text-align: left;
}
.neutral {
color: black;
font-style: normal;
font-weight: normal;
font-variant: normal;
}
.nightMode .neutral {
color: white;
}
.HYPHENATION {
color: red;
font-size: 160%;
font-weight: bold;
}
.Sense {
display: block;
margin-left: 20px;
margin-bottom: 15px;
}