Toggle Visibility of Solutions in Bookdown

You have a bookdown book with exercises and solutions to those exercises. You would like to control visibility of the solutions. For example, when the bookdown file loads, you would like all the solutions to be hidden. You would like a button for each solution to toggle its visibility. How to do this? We faced this problem when preparing materials for our CVXR tutorial.

A little Javascript can easily solve this problem.

Example

We will use the minimal book that comes with Rstudio when you create a new bookdown project as an example. Go ahead and edit the file 01-intro.Rmd adding the following two exercises towards the end.

We first create a header.html file to include some Javascript in the main directory (the same directory containing 01-intro.Rmd) with the following contents, which I’ve intentionally made verbose for legibility.

<script>
    $(document).ready(function () {
        process_solutions();
    });
    function process_solutions() {
        $("div.section[id^='solution']").each(function(i) {
        var soln_wrapper_id = "cvxr_ex_" + i;
        var solution_id = $(this).attr('id');
        var button = $("<button onclick=\"toggle_solution('" + soln_wrapper_id + "')\">Show/Hide</button>");
        var new_div = $("<div id='" + soln_wrapper_id + "' class='solution' style='display: none;'></div>");
        var h = $(this).first();
        var others = $(this).children().slice(1);
        $(others).each(function() {
            $(this).appendTo($(new_div));
        });
        $(button).insertAfter($(h));
        $(new_div).insertAfter($(button));
        })
    }
    function toggle_solution(el_id) {
      $("#" + el_id).toggle();
    } 
</script>

The functions above use jquery API calls. When the document is loaded in a browser, the process_solutions call will modify the DOM tree to hide the solutions. First it locates all the solution sections and for each such section does the following.

  1. Generates an id (soln_wrapper_id) which will be used for a newly created div element (new_div) with no visibility by default;
  2. Creates a button element with text Show/Hide to toggle display of any element specified by an id (el_id);
  3. Locates the first header section in the solution (h) and the rest of the elements that follow the header (others);
  4. Moves the others into new_div;
  5. Inserts the button after the header;
  6. Finally, moves the new_div after the button (button).

To include this header file, we need to modify _output.yml to include the lines below in the bookdown::gitbook section, at the right level of indentation (that of css, config).

  includes:
    in_header: header.html

Try it

My source for this example is in my github repo.

If you now preview the book, you will see something like this where you can toggle the solutions on and off.

Extensions

If the book is served from a host controlled by the instructor, I would think it possible to have solutions toggled on and off by the instructor for student viewing. I would think that the Rstudio folks would be able to make such a thing work with their RStudio Connect product.