Search code examples
javascriptrr-markdownquarto

Quarto website - make template for javascript button ("show answer")


I am making a website with Quarto. I can add Javascript and HTML code in the quarto document (.qmd file). I am not well-versed in Javascript, but found the following:

<button class="show-answer" onclick="showAnswer('answer1')"> Show answer</button>
<p id="answer1" style="display: none;">Secret answer 1</p>

<button class="show-answer" onclick="showAnswer('answer2')"> Show answer</button>
<p id="answer2" style="display: none;">Secret answer 2</p>

<script>
function showAnswer(answerId) {
  var answer = document.getElementById(answerId);
  if (answer.style.display === "none") {
    answer.style.display = "block";
  } else {
    answer.style.display = "none";
  }
}
</script>

This works. The webpage, however, requires this button many times. Is it possible to make some kind of template, so I can write in the Quarto document (for example)

[answer] Secret answer [/answer]

The Javascript also seems to require some id, e.g. id="answer1". Is it possible to automate this id generation if I am using a template?


Solution

  • You can write a JS function that will replace a div element (created with pandoc divs ::: {.class}) with answers or a span element (created with pandoc spans []{.class}) with answers with the structure below,

    <button class="show-answer" onclick="showAnswer('answer1')"> Show answer</button>
    <p id="answer1" style="display: none;">Secret answer 1</p>
    

    Full Reprex

    add-answer-button.html

    <script>
      
      function addAnswerWithButton(answerClass, buttonText, answerText) {
        
        const divElement = document.querySelector(`.${answerClass}`);
        const randomId = "answer-" + Math.floor(Math.random() * 1000000);
      
        // Create the new button element
        const buttonElement = document.createElement("button");
        buttonElement.className = "show-answer";
        buttonElement.textContent = buttonText;
        buttonElement.onclick = function() {
          showAnswer(randomId);
        };
      
        // Create the new answer element
        const answerElement = document.createElement("p");
        answerElement.id = randomId;
        answerElement.style.display = "none";
        answerElement.innerHTML = answerText;
      
        // Replace the div element with the new button and answer elements
        divElement.parentNode.replaceChild(buttonElement, divElement);
        buttonElement.insertAdjacentElement("afterend", answerElement);
      }
      
      function showAnswer(answerId) {
        var answer = document.getElementById(answerId);
        if (answer.style.display === "none") {
          answer.style.display = "block";
        } else {
          answer.style.display = "none";
        }
      }
    
      function addAnswerButton() {
        var answers = document.querySelectorAll(".answer")
        answers.forEach(e => {
          addAnswerWithButton("answer", " Show Answer", e.innerHTML);
        });
      }
      
      window.document.addEventListener("DOMContentLoaded", function (event) {
        addAnswerButton();
      });
    </script>
    

    test.qmd

    ---
    title: Answer Button
    format: html
    include-in-header: add-answer-button.html
    ---
    
    ## Exercise
    
    Question1: What is 1 + 1? [Answer: 2]{.answer}
    
    Question2: Some question? 
    
    [This is some answer to question 2]{.answer}
    
    
    Question3: Some more question? 
    
    ::: {.answer}
    
    This is some answer to question 3
    
    :::
    

    Here you can write answers within squares bracket and then assign the .answer class to it (e.g. [some answer]{.answer} and it will be replaced with a button (See the rendered output below), and also you can write the answers wrapped within the ::: and assign the class .answer to the div.


    answers with button