Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
273 views
in Technique[技术] by (71.8m points)

r - How to add code folding to output chunks in rmarkdown html documents

I really appreciate the "code_folding" feature in RMarkdown. However, what I really need is to have the code show all the time and toggle the display on the output.

---
title: "test file"
author: "dayne"
date: "June 10, 2016"
output: 
  html_document:
    code_folding: hide
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

Here is a basic example.

```{r}
3 + 4
```

Is there a way to toggle the output rather than the code? The best (but not ideal) solution I have thought of is to add collapse=TRUE to the chunks, but then the code and the output still display at the same time.

Link to the compiled document: http://rpubs.com/daynefiler/188408

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

TOC:

  1. Full control over which chunks should be folded

  2. Fold all chunks that contain more than one line of code/output


1. Full control over which chunks should be folded

I wanted to have the same functionality as well and did the following:

I created a JavaScript that looks as follows:

$(document).ready(function() {

  $chunks = $('.fold');

  $chunks.each(function () {

    // add button to source code chunks
    if ( $(this).hasClass('s') ) {
      $('pre.r', this).prepend("<div class="showopt">Show Source</div><br style="line-height:22px;"/>");
      $('pre.r', this).children('code').attr('class', 'folded');
    }

    // add button to output chunks
    if ( $(this).hasClass('o') ) {
      $('pre:not(.r)', this).has('code').prepend("<div class="showopt">Show Output</div><br style="line-height:22px;"/>");
      $('pre:not(.r)', this).children('code:not(r)').addClass('folded');

      // add button to plots
      $(this).find('img').wrap('<pre class="plot"></pre>');
      $('pre.plot', this).prepend("<div class="showopt">Show Plot</div><br style="line-height:22px;"/>");
      $('pre.plot', this).children('img').addClass('folded');

    }
  });

  // hide all chunks when document is loaded
  $('.folded').css('display', 'none')

  // function to toggle the visibility
  $('.showopt').click(function() {
    var label = $(this).html();
    if (label.indexOf("Show") >= 0) {
      $(this).html(label.replace("Show", "Hide"));
    } else {
      $(this).html(label.replace("Hide", "Show"));
    }
    $(this).siblings('code, img').slideToggle('fast', 'swing');
  });
});

Since I am no JS crack it might not be perfect, but it does what it is supposed to. Include it in your Rmd file:

<script src="js/hideOutput.js"></script>

I also wrote some CSS definitions to style the button:

.showopt {
  background-color: #004c93;
  color: #FFFFFF; 
  width: 100px;
  height: 20px;
  text-align: center;
  vertical-align: middle !important;
  float: right;
  font-family: sans-serif;
  border-radius: 8px;
}

.showopt:hover {
    background-color: #dfe4f2;
    color: #004c93;
}

pre.plot {
  background-color: white !important;
}

After including both, the JS file and the stylesheet, you can hide chunks by wrapping a div container around them with one of the following classes:

Hide output only

<div class="fold o">
```{r}
  ...
```
</div>

Hide source code

<div class="fold s">
```{r}
  ...
```
</div>

Hide both

<div class="fold s o">
```{r}
  ...
```
</div>

The script detects the type of each chunk (e.g. source code, text output or plot output) and labels the buttons accordingly.

The result looks like this:

enter image description here

enter image description here


2. Fold all chunks that contain more than one line of code/output

Here is a version of the script that adds the folding feature to all chunks that are longer than one line:

$(document).ready(function() {
  $plots = $('img.plot');
  $chunks = $('pre').has('code');
  $chunks = $chunks.filter(function(idx) {
    return $(this).children('code').outerHeight(false) > parseInt($(this).css('line-height'));
  });

  $chunks.each(function () {
    if($(this).hasClass('r')) {
      $(this).append("<div class="showopt">Show Source</div><br style="line-height:22px;"/>");
    } else {
      $(this).append("<div class="showopt">Show Output</div><br style="line-height:22px;"/>");
    }
  });

  $plots.each(function () {
    $(this).wrap('<pre class="plot"></pre>');
    $(this).parent('pre.plot').prepend("<div class="showopt">Show Plot</div><br style="line-height:22px;"/>");
  });

  // hide all chunks when document is loaded
  $chunks.children('code').toggle();
  $('pre.plot').children('img').toggle();
  // function to toggle the visibility
  $('.showopt').click(function() {
    var label = $(this).html();
    if (label.indexOf("Show") >= 0) {
      $(this).html(label.replace("Show", "Hide"));
    } else {
      $(this).html(label.replace("Hide", "Show"));
    }
    $(this).siblings('code, img').slideToggle('fast', 'swing');
  });
});

Just include it with <script src="js/hideAll.js"></script> and you don't need to wrap div containers around your code chunks. One thing you have to add in your Rmd document though is the following global chunk option:

```{r, echo = F}
knitr::opts_chunk$set(out.extra = 'class="plot"')
```

It is needed to identify graphical output.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...