Java script on front card stops working on second card

Hello!

I’ve been using a java script on the front side of my cards to randomly choose a font face from a font list I have in the “styling” tab. It used to work before, but I’m not sure when it stopped working (could be like half a year even when switching laptops). I’m using Anki 26.08.1 on a Linux system and AnkiDroid 2.24.0.

It works on the first card when I open a deck, but stops working from the second card onwards.

The same thing happens when going to to card template window (where you can write the front, back and styling). It works when opening the window, but when switching to the back side and back to the front side, it doesn’t work anymore.

The same thing happens in AnkiDroid. First card ok, rest of cards show no java script.

Any ideas?

Front Template:

<div class="outerbox">
<div id="front"><p class="Entry">{{Entry}}</p>
<p class="Wordtype">{{Wordtype}}</p>
<br>
<br>
<div class="Sentence-JP">{{kanji:Sentence-JP}}</div></div></div>
<br>
<div id="zahl"/>

<script type="text/javascript">
 style = document.getElementById("front").style;

 let n = Math.floor(Math.random() * 12);
 style.fontSize = "8px";
 style.fontFamily = "random_" + n;

document.getElementById("zahl").innerHTML=n; 
</script>

Back Template:

<div class="outerbox">
{{Pitch}}
<a style="text-decoration:none" href="kanjistudy://search?q={{Entry}}"><div class="Entry">{{furigana:Entry-Reading}}</div></a>
<br>
<p class="Wordtype">{{Wordtype}}</p>

{{#Entry-JP}}
<div class="jpdef-box">
   {{furigana:Entry-JP}}
  {{#Structure}}<hr class="new1">
<div class="grammar">{{Structure}}</div>{{/Structure}}
</div>
<p class="Entry-EN">{{furigana:Entry-EN}}</p>
{{/Entry-JP}}

{{^Entry-JP}}
  {{#Structure}}<div class="grammar-box">{{Structure}}</div>{{/Structure}}
<p class="Entry-EN">{{furigana:Entry-EN}}</p>
{{/Entry-JP}}

{{#Picture}}
   {{Picture}}
{{/Picture}}<br>

<div class="Sentence-JP">
    {{furigana:Sentence-JP}}
</div>
<p class="Sentence-EN">{{hint:Sentence-EN}}
</p>
<center>{{Entry-Audio}}{{Sentence-Audio}}</center>
</div>

Styling:

@font-face{font-family:random_0;  src:url('_AozoraMinchoRegular.ttf');}
@font-face{font-family:random_1;  src:url('_DotGothic16-Regular.ttf');}
@font-face{font-family:random_2;  src:url("_TakaoMjMincho.ttf");} 
@font-face{font-family:random_3;  src:url('_ZenMaruGothic-Regular.ttf');}
@font-face{font-family:random_4;  src:url('_YuseiMagic-Regular.ttf');}
@font-face{font-family:random_5;  src:url('_KosugiMaru-Regular.ttf');}
@font-face{font-family:random_6;  src:url("_HGSKyokashotai.ttf");}
@font-face{font-family:random_7;  src:url("_ZenKurenaido-Regular.ttf");}
@font-face{font-family:random_8;  src:url("_KleeOne-SemiBold.ttf");}
@font-face{font-family:random_9;  src:url('_MPLUSRounded1c-Regular.ttf');}
@font-face{font-family:random_10; src:url('_SoukouMincho.ttf');}
@font-face { font-family:random_11; src: url("_FCKyokashotai.ttf"); }
@font-face{font-family:random_12; src:url('_HanaMinA.ttf');}

@font-face {font-family: KleeOne-Regular; src: url("_KleeOne-Regular.ttf"); }

@font-face {font-family: KleeOne-SemiBold; src: url("_KleeOne-SemiBold.ttf"); }

@font-face {font-family: KanjiStrokeOrders; src: url("_KanjiStrokeOrders.ttf"); }

.card {
 font-size: 24px;
 font-family: KleeOne-SemiBold;
 text-align: center;
 word-wrap: break-word;
}

div, a {
 color: var(--text-color); }
.card a { text-decoration-color: #A1B2BA; }

.nightMode svg.pitch { filter: invert(1); }

.Entry { #font-family: KanjiStrokeOrders; font-size: 70px; }
.Wordtype { font-size: 12px; font-style: italic; color: #bbbbbb; }
.grammar { font-size: 14px; color: #dddddd; }
.Entry-EN { font-size: 14px; text-align: center; } 
.notes { font-size: 15px; text-align:left; } 
.Sentence-EN { font-size: 14px;  text-align: left; }
.Sentence-JP { font-size: 22px; text-align: left;  } 

.en { font-size: 16px; }
.hint { font-size: 14px; text-decoration:none; text-align: left; }

b { color: #90ee90; }
hr.new1 {  border: 1px dashed #000000; }
img { max-height: 200px;}
.replay-button svg { width: 30px;  height: 30px; }

zahl { color: #bbbbbb; } 

.outerbox {
  display: inline-block;
  width: 60%;
  #border: 1px solid; 
  border-color: #777777; 
}

.jpdef-box {
  display: inline-block;
  #width: 80%;
  background-color: #202020;
  border: 1px solid; 
  border-color: #777777; 
  border-radius: 10px;
  padding: 8px 15px;
  text-align: left;
  font-size: 20px;
  color: white; 
}

.grammar-box {
  display: inline-block;
  width: 60%;
  background-color: #202020;
  border: 1px solid; 
  border-color: #777777; 
  border-radius: 10px;
  padding: 8px 15px;
  text-align: left;
  font-size: 14px;
  color: #dddddd
}

.mobile .outerbox {
  display: inline-block;
  width: 100%; 
  #border: 1px solid; 
  border-color: #777777; 
}

.mobile .jpdef-box {
  display: inline-block;
  width: auto;
  background-color: #202020;
  border: 1px solid; 
  border-color: #777777; 
  border-radius: 10px;
  padding: 8px 15px;
  text-align: left;
  font-size: 20px;
}

.mobile .grammar-box {
  display: inline-block;
  width: auto;
  background-color: #202020;
  border: 1px solid; 
  border-color: #777777; 
  border-radius: 10px;
  padding: 8px 15px;
  text-align: left;
  font-size: 14px;
  color: #dddddd
}

That’s because of the line

 let n = Math.floor(Math.random() * 12);

Anki cards are executed in the same scope and let does not allow re-declaring variables (which is what your second card tries to do after n being initialized by the first card). You can use var n = ... or simply n = ... instead (or window.n = ..., or, better, wrap it in a block to localize the scope, etc.)

This shouldn’t have ever worked on desktop. It might have worked on AnkiDroid with the old review screen, because it used to reload the whole webview on each card flip, but the desktop Anki and the new AnkiDroid review screen both only replace card content inside the same reviewer page, so the card scripts are placed in the same scope, which can cause conflicts if not treated appropriately.


On a side note, if you are writing your own scripts, you might find the inspector add-on very instrumental. It allows you viewing the console log, so errors become much easier to analyze and fix.

Thank you! Using var n = … produced the same behaviour, but just n = … seems to be working!

And also thanks for explaining why it worked on AnkiDroid before! I thought it worked on desktop too, but maybe I did all my reviews on my phone at that time, so I may have confused it.

That might be because you didn’t reset the webview after removing the previous version, so var ended up trying to re-declare the variable that was already created by let in that scope. If you change let to var and then restart the reviewer, it should work without issues.

Desktop used to reload the webview every 100 cards a few years ago: