Temporary memos

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;
}