I’ve searched through suggestions and add-ons, and haven’t seen anything that seems like it would work for this. Often, when I’m going through my deck I want to add a temporary memo to a card. Sometimes this is a reminder that this particular sentence is in a new grammatical form (but still has the same English translation), or there’s some nuance that I want to look into later. I could just create an empty field and edit cards as I go through, but I think a more graceful solution would to be to have a memo field to jot things down in. I’d envision a text field, a toggle for the memo to appear on the front or back, and a radio for it to auto-delete: never, after next viewing, or after card is marked easy.
1 Like
This might be of interest to you Reddit - The heart of the internet.
2 Likes
That seems like a fine solution to me – because that’s what I do. [See my comments / questions / concerns in that reddit thread.]
This is generally what I’m looking for, but it’s not available on AnkiDroid.
I went ahead and created a card template version of this idea. A lot of the code is AI generated and a little hacky, but it works.
Front
<div id="front-content">
<!-- Main front content -->
<div id="sentence">{{Front}}</div>
<!-- Front memo placeholder -->
<div id="memoDisplayFront" style="
margin:10px auto;
text-align:center;
align-items:center;
font-style:italic;
color:#555;
max-width:400px;
position:relative;
font-family:sans-serif;
font-size:14px;
line-height:1.4em;
"></div>
</div>
<script>
(function() {
const cardKey = btoa(unescape(encodeURIComponent("{{Front}}" + "||" + "{{Back}}")));
const memoDisplayFront = document.getElementById("memoDisplayFront");
let stored = JSON.parse(localStorage.getItem(cardKey) || '{}');
// Handle "delete on next" and "after3" counter
if ((stored.deleteMode === 'next' || stored.deleteMode === 'after3') && typeof stored.counter !== 'undefined') {
if (stored.counter <= 0) {
stored.text = '';
stored.counter = undefined; // reset counter after deletion
} else {
stored.counter--; // decrement for next viewing
}
localStorage.setItem(cardKey, JSON.stringify(stored));
}
// Display memo if it exists and should appear on front
if (stored.text && stored.location === 'front') {
memoDisplayFront.textContent = stored.text;
}
})();
</script>
Back
{{FrontSide}}
<hr id="answer">
<div id="backContent">{{Back}}</div>
<div id="memoDisplayBack" style="
margin:10px auto;
text-align:center;
font-style:italic;
color:#555;
max-width:400px;
position:relative;
font-family:sans-serif;
font-size:14px;
line-height:1.4em;
"></div>
<button id="createMemoBtn" style="
display:none;
margin-top: 4em;
margin:5px auto;
padding:4px 8px;
border-radius:6px;
border:1px solid #ccc;
background:#f5f5f5;
cursor:pointer;
font-size:12px;
">Create Memo</button>
<div id="memoContainer" style="
display:none;
margin-top: 4em;
margin:10px auto;
background:#fefefe;
border:1px solid #ccc;
border-radius:8px;
padding:10px;
box-shadow:0 4px 12px rgba(0,0,0,0.1);
max-width:600px;
font-family:sans-serif;
gap:10px;
">
<textarea id="memoText" rows="4" placeholder="Write a memo..." style="
flex:2;
width:100%;
resize:none;
padding:8px;
font-size:14px;
border-radius:6px;
border:1px solid #ccc;
box-sizing:border-box;
box-shadow:inset 0 1px 3px rgba(0,0,0,0.1);
"></textarea>
<div id="controlPanel" style="
flex:1;
display:flex;
flex-direction:column;
justify-content:flex-start;
border-left:1px solid #ccc;
margin-left:10px;
height:100%;
">
<div id="memoOptions" style="flex:none; text-align:center; border-bottom:1px solid #ccc; padding:4px 0; background:#fafafa; border-radius:6px 6px 0 0;">
<strong style="font-size:12px; display:block; margin-bottom:2px;">Auto-delete:</strong>
<div class="btn-group" id="deleteGroup" style="display:flex; gap:0;">
<button data-value="never" class="btnOption">Never</button>
<button data-value="next" class="btnOption">Next</button>
<button data-value="after3" class="btnOption">After 3</button>
</div>
</div>
<div id="displayOptions" style="flex:none; text-align:center; border-bottom:1px solid #ccc; padding:4px 0; background:#fafafa;">
<strong style="font-size:12px; display:block; margin-bottom:2px;">Show on:</strong>
<div class="btn-group" id="locationGroup" style="display:flex; gap:0;">
<button data-value="front" class="btnOption">Front</button>
<button data-value="back" class="btnOption">Back</button>
</div>
</div>
<div style="flex:none; display:flex; justify-content:center; gap:2px; border-top:1px solid #ccc; margin-top:2px; background:#fafafa;">
<button id="deleteMemoBtn" style="
flex:1;
padding:4px 0;
margin:2px 0 0 0;
border-radius:0 0 0 6px;
border:1px solid #ccc;
background:#fdd;
cursor:pointer;
font-weight:bold;
color:#700;
box-shadow:0 2px 4px rgba(0,0,0,0.1);
">🗑 Delete</button>
<button id="closeMemoBtn" style="
flex:1;
padding:4px 0;
margin:2px 0 0 0;
border-radius:0 0 6px 0;
border:1px solid #ccc;
background:#f5f5f5;
cursor:pointer;
font-weight:bold;
color:#333;
box-shadow:0 2px 4px rgba(0,0,0,0.1);
">❌ Close</button>
</div>
</div>
</div>
<style>
.btn-group .btnOption {
flex:1;
padding:5px 0;
border:1px solid #ccc;
background:#f5f5f5;
cursor:pointer;
font-size:12px;
margin:0;
border-radius:0;
transition:all 0.15s;
color:#555;
box-shadow:0 2px 3px rgba(0,0,0,0.1);
}
.btn-group .btnOption:first-child { border-top-left-radius:4px; border-bottom-left-radius:4px; }
.btn-group .btnOption:last-child { border-top-right-radius:4px; border-bottom-right-radius:4px; }
.btn-group .btnOption.active {
background:#a8a8a8;
color:#f0f0f0;
box-shadow:inset 0 2px 4px rgba(0,0,0,0.3);
}
.btn-group .btnOption:hover { background:#e0e0e0; }
</style>
<script>
(function(){
const INITIAL_DELAY = 10;
const RETRY_INTERVAL = 200;
const MAX_RETRIES = 20;
function initMemo(attempt = 1) {
const createBtn = document.getElementById("createMemoBtn");
const memoContainer = document.getElementById("memoContainer");
const memoDisplayBack = document.getElementById("memoDisplayBack");
const memoText = document.getElementById("memoText");
const closeBtn = document.getElementById("closeMemoBtn");
const deleteBtn = document.getElementById("deleteMemoBtn");
const deleteGroup = document.getElementById("deleteGroup");
const locationGroup = document.getElementById("locationGroup");
const frontDiv = document.getElementById("memoDisplayFront");
if (!createBtn || !memoContainer || !memoDisplayBack || !memoText || !closeBtn || !deleteBtn || !deleteGroup || !locationGroup || !frontDiv) {
if (attempt < MAX_RETRIES) setTimeout(() => initMemo(attempt + 1), RETRY_INTERVAL);
return;
}
const cardKey = btoa(unescape(encodeURIComponent("{{Front}}" + "||" + "{{Back}}")));
let stored = JSON.parse(localStorage.getItem(cardKey) || '{}');
stored.text = stored.text || '';
stored.deleteMode = stored.deleteMode || 'never';
stored.location = stored.location || 'back';
stored.counter = stored.counter || undefined;
stored.after3Number = stored.after3Number || 3;
function refreshDisplay() {
stored = JSON.parse(localStorage.getItem(cardKey) || '{}');
frontDiv.textContent = '';
memoDisplayBack.textContent = '';
const existingEdit = document.getElementById('editMemoBtn');
if (existingEdit) existingEdit.remove();
const activeDiv = stored.location === 'front' ? frontDiv : memoDisplayBack;
activeDiv.textContent = stored.text || '';
if (stored.text && stored.text.length > 0) {
const newBtn = document.createElement('button');
newBtn.id = 'editMemoBtn';
newBtn.textContent = '✎';
newBtn.style.border = 'none';
newBtn.style.background = 'transparent';
newBtn.style.cursor = 'pointer';
newBtn.style.fontSize = '16px';
newBtn.addEventListener('click', () => {
memoContainer.style.display = 'flex';
memoText.value = stored.text;
});
activeDiv.appendChild(newBtn);
}
createBtn.style.display = stored.text ? 'none' : 'block';
[...deleteGroup.children].forEach(btn => btn.classList.toggle('active', btn.dataset.value === stored.deleteMode));
[...locationGroup.children].forEach(btn => btn.classList.toggle('active', btn.dataset.value === stored.location));
}
refreshDisplay();
createBtn.addEventListener('click', () => {
createBtn.style.display = 'none';
memoContainer.style.display = 'flex';
memoText.value = '';
});
closeBtn.addEventListener('click', () => {
memoContainer.style.display = 'none';
stored = JSON.parse(localStorage.getItem(cardKey) || '{}');
if (!stored.text) createBtn.style.display = 'block';
});
deleteBtn.addEventListener('click', () => {
let cur = JSON.parse(localStorage.getItem(cardKey) || '{}');
cur.text = '';
delete cur.counter;
cur.deleteMode = 'never';
cur.deleteNextView = false;
cur.deleteOnEasy = false;
localStorage.setItem(cardKey, JSON.stringify(cur));
memoContainer.style.display = 'none';
createBtn.style.display = 'block';
refreshDisplay();
});
function setupGroup(group) {
[...group.children].forEach(btn => {
btn.addEventListener('click', () => {
[...group.children].forEach(b => b.classList.remove('active'));
btn.classList.add('active');
saveMemo(btn.dataset.value);
});
});
}
setupGroup(deleteGroup);
setupGroup(locationGroup);
function saveMemo(selectedValue) {
let cur = JSON.parse(localStorage.getItem(cardKey) || '{}');
const deleteMode = selectedValue || [...deleteGroup.children].find(b => b.classList.contains('active'))?.dataset.value || 'never';
const location = [...locationGroup.children].find(b => b.classList.contains('active'))?.dataset.value || 'back';
cur.text = memoText.value;
cur.deleteMode = deleteMode;
cur.location = location;
if (deleteMode === 'next') cur.counter = 2;
else if (deleteMode === 'after3') cur.counter = 6; // After 3 views × 2
else delete cur.counter;
localStorage.setItem(cardKey, JSON.stringify(cur));
refreshDisplay();
}
memoText.addEventListener('input', () => saveMemo());
setTimeout(() => {
const buttons = document.querySelectorAll(".btn,.review-btn");
buttons.forEach(btn => {
btn.addEventListener("click", () => {
let d = JSON.parse(localStorage.getItem(cardKey) || '{}');
if (d.deleteOnEasy) {
d.text = '';
d.deleteOnEasy = false;
if (typeof d.counter !== 'undefined') delete d.counter;
localStorage.setItem(cardKey, JSON.stringify(d));
refreshDisplay();
}
});
});
}, 10);
}
setTimeout(() => initMemo(), INITIAL_DELAY);
})();
</script>
CSS
/* Front side sentence */
#sentence {
margin-top: 4em;
text-align: center;
font-size: 1.4em;
line-height: 1.6em;
}
/* Back side sentence */
#backContent {
margin-top: 1em;
text-align: center;
font-size: 1.4em;
line-height: 1.6em;
}
/* Segments behave like inline text */
.segment {
position: relative;
cursor: pointer;
border-radius: 4px;
padding: 0 2px; /* minimal padding */
}
.segment:hover {
background: #e7f3ff;
}
/* Popup tooltip */
.popup {
visibility: hidden;
opacity: 0;
position: absolute;
bottom: 120%;
left: 50%; /* default anchor is center */
transform: translateX(-50%);
background: #333;
color: #fff;
padding: 6px 8px;
border-radius: 6px;
white-space: normal;
min-width: 40vw;
max-width: 80vw;
text-align: center;
font-size: 0.95em;
z-index: 999;
transition: opacity 0.2s;
box-sizing: border-box;
word-wrap: break-word;
}
.popup::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #333 transparent transparent transparent;
}
.popup.show {
visibility: visible;
opacity: 1;
}
/* Back side full translation */
#backtext {
margin-top: 2em;
text-align: center;
font-size: 1.3em;
color: #444; /* dark grey */
line-height: 1.6em;
}