1

I was able to put successfully in practice the solution presented by @SébastienRochette at this StacOverflow post, to add show/hide buttons to R, Python and Bash code blocks in bookdown books. it works great and also allows you to customize the background color of the chunk and the buttons as well.

The one thing I am not satisfied is that I had to duplicate the Javascript code for each of the languages I am using. So, my code in codefolding.js looks like this:

window.initializeCodeFolding = function(show) {

  // handlers for show-all and hide all
  $("#rmd-show-all-code").click(function() {
    // close the dropdown menu when an option is clicked
    $("#allCodeButton").dropdown("toggle");
      // R show code
      $('div.r-code-collapse').each(function() {
        $(this).collapse('show');
      });
      // Python show code
      $('div.py-code-collapse').each(function() {
        $(this).collapse('show');
      }); 
      // Bash show code
      $('div.sh-code-collapse').each(function() {
        $(this).collapse('show');
      }); 
      
  });
  $("#rmd-hide-all-code").click(function() {
      // close the dropdown menu when an option is clicked
      $("#allCodeButton").dropdown("toggle");
      // Hide R code
      $('div.r-code-collapse').each(function() {
        $(this).collapse('hide');
      });
      // Hide Python code
      $('div.py-code-collapse').each(function() {
        $(this).collapse('hide');
      });
      // Hide Bash code
      $('div.sh-code-collapse').each(function() {
        $(this).collapse('hide');
      });
  });


  // index for unique code element ids
  var r_currentIndex  = 1;   // for R code
  var py_currentIndex = 1;   // for Python code
  var sh_currentIndex  = 1;   // for shell code

  // select Python chunks
  var pyCodeBlocks = $('pre.python');
  pyCodeBlocks.each(function() {
    // create a collapsable div to wrap the code in
    var div = $('<div class="collapse py-code-collapse"></div>');
    if (show)
      div.addClass('in');
    var id = 'pycode-643E0F36' + py_currentIndex++;
    div.attr('id', id);
    // "this" refers the code chunk
    $(this).before(div);
    $(this).detach().appendTo(div);
    $(this).css('background-color','#ebfaeb');  // change color of chunk background
    
    // add a show code button right above
    var showCodeText = $('<span>' + (show ? 'Hide Python code' : 'Python code') + '</span>');
    var showCodeButton = $('<button type="button" class="btn btn-default btn-xs code-folding-btn pull-right"></button>');
    showCodeButton.append(showCodeText);
    showCodeButton
        .attr('data-toggle', 'collapse')
        .attr('data-target', '#' + id)
        .attr('aria-expanded', show)
        .attr('aria-controls', id);    
        
    // change the background color of the button
    showCodeButton.css('background-color','#009900');
        
    var buttonRow = $('<div class="row"></div>');
    var buttonCol = $('<div class="col-md-12"></div>');
    buttonCol.append(showCodeButton);
    buttonRow.append(buttonCol);
    div.before(buttonRow);    
    
    // update state of button on show/hide
    div.on('hidden.bs.collapse', function () {
      showCodeText.text('Python code');
    });
    div.on('show.bs.collapse', function () {
      showCodeText.text('Hide Python code');
    });  
   });
  
  

  // select Bash shell chunks
  var shCodeBlocks = $('pre.bash');
  shCodeBlocks.each(function() {
    // create a collapsable div to wrap the code in
    var div = $('<div class="collapse sh-code-collapse"></div>');
    if (show)
      div.addClass('in');
    var id = 'shcode-643E0F36' + sh_currentIndex++;
    div.attr('id', id);
    // "this" refers the code chunk
    $(this).before(div);
    $(this).detach().appendTo(div);
    $(this).css('background-color','#A0A0A0');  // change color of chunk background
    
    // add a show code button right above
    var showCodeText = $('<span>' + (show ? 'Hide Bash code' : 'Bash code') + '</span>');
    var showCodeButton = $('<button type="button" class="btn btn-default btn-xs code-folding-btn pull-right"></button>');
    showCodeButton.append(showCodeText);
    showCodeButton
        .attr('data-toggle', 'collapse')
        .attr('data-target', '#' + id)
        .attr('aria-expanded', show)
        .attr('aria-controls', id);    
        
    // change the background color of the button
    showCodeButton.css('background-color','#cc7a00');
        
    var buttonRow = $('<div class="row"></div>');
    var buttonCol = $('<div class="col-md-12"></div>');
    buttonCol.append(showCodeButton);
    buttonRow.append(buttonCol);
    div.before(buttonRow);    
    
    // update state of button on show/hide
    div.on('hidden.bs.collapse', function () {
      showCodeText.text('Bash code');
    });
    div.on('show.bs.collapse', function () {
      showCodeText.text('Hide Bash code');
    });  
   });  


  // select all R code blocks
  // var rCodeBlocks = $('pre.sourceCode, pre.r, pre.bash, pre.sql, pre.cpp, pre.stan');
  // adding pre.sourceCode confuses the Python button
  var rCodeBlocks = $('pre.r, pre.sql, pre.cpp, pre.stan');
  rCodeBlocks.each(function() {
    // create a collapsable div to wrap the code in
    var div = $('<div class="collapse r-code-collapse"></div>');
    if (show)
      div.addClass('in');
    var id = 'rcode-643E0F36' + r_currentIndex++;
    div.attr('id', id);
    $(this).before(div);
    $(this).detach().appendTo(div);
    $(this).css('background-color','#e6faff'); // change color of chunk background

    // add a show code button right above
    var showCodeText = $('<span>' + (show ? 'Hide R code' : 'R code') + '</span>');
    var showCodeButton = $('<button type="button" class="btn btn-default btn-xs code-folding-btn pull-right"></button>');
    showCodeButton.append(showCodeText);
    showCodeButton
        .attr('data-toggle', 'collapse')
        .attr('data-target', '#' + id)
        .attr('aria-expanded', show)
        .attr('aria-controls', id);
    
    // change the background color of the button        
    showCodeButton.css('background-color','#0000ff');
    
    var buttonRow = $('<div class="row"></div>');
    var buttonCol = $('<div class="col-md-12"></div>');
    buttonCol.append(showCodeButton);
    buttonRow.append(buttonCol);
    div.before(buttonRow);

    // update state of button on show/hide
    div.on('hidden.bs.collapse', function () {
      showCodeText.text('R code');
    });
    div.on('show.bs.collapse', function () {
      showCodeText.text('Hide R code');
    });
  });

}

The working solution is part of the Minimal rTorch Book which can be read online here and its GitHub repository here.

The way this works in bookdown is by writing a R script hide_code.R, that appends the Javascript code and CSS as a header in each of the Rmd files from _output.yml.

bookdown::gitbook:
  #dev: svglite # in case I need svg images
  includes:
    in_header: hide_code.html

I seldom use Javascript, and I was wondering how would you modify this code to serve the multiple languages that knitr recognizes based on the engine used inside the knitr chunk.

Note. I am attaching screenshots of how the knitr blocks look like after applying this hide/show buttons.

R code chunk

Python code

Bash code

f0nzie
  • 1,086
  • 14
  • 17

0 Answers0