6
votes

In recent years, it has been possible to add numbering to the figure captions on R chunks in html_document2, as outlined in Yihui Xie's online text for bookdown. However, it has been more difficult to preserve this numbering while also having custom figures output from these chunks. For example, trying to create the custom figures using CSS flexbox from Carson Sievert's online text.

There are several other threads that discuss numbering HTML Rmd figures and using hooks or CSS counters to add custom numbering. However, I could not find a solution that allowed for a custom figure as well as html_document2 numbering to be preserved.

In the example below, I want a side-by-side plotly graphic to be reactive to screen size but also have the same figure caption and numbering.

---
output: 
  bookdown::html_document2:
    self-contained: TRUE
---

```{css, echo=FALSE}
#dualpanel {
  width: 50%
}

@media screen and (max-width: 500px) {
  #dualpanel {
    width: 100%
  }}
```
```{r chunk1, echo=FALSE, htmlcap='FIRST FIGURE CAP'}
temp <- plotly::plot_ly(mtcars, x = ~cyl, y=~mpg)
shiny::div(class = 'figure',
  style = "display: flex; flex-wrap: wrap; justify-content: center",
  shiny::div(temp, id = 'dualpanel'), 
  shiny::div(temp, id = 'dualpanel'))
```

```{r chunk2,  echo=FALSE, fig.cap='SECOND FIGURE CAP'}
plot(mtcars$cyl, mtcars$mpg)
```

This creates an output like this:

enter image description here

1

1 Answers

6
votes

In order to address this issue, it is important to know that knitr uses #fig:label in pandoc in order to create the automated numbering in the output. You can see this when you examine the markdown temporary output from the knitr process:

<div class="figure">
<img src="web_tiles_v2_files/figure-html/otherchunk-1.png" alt="SECOND FIGURE CAP"  />
<p class="caption">(\#fig:SPECIFICCHUNKOFINTEREST)SECOND FIGURE CAP</p>
</div>

As such, one should be able to maintain the customized figure output by adjusting the custom hook seen in other solutions. This would look like the following, just make sure you have the right chunk label included:

```{r}
knit_hooks$set(customcap= function(before, options, envir) {
  if(!before) {
    paste('<p class="caption"> (\\#fig:chunk1)',options$customcap,"</p>",sep="")
    }
    })
```

One could also use some of the internal knitr functions to automatically grab the fig.lp and label: paste('<p class="caption">', knitr:::create_label(options$fig.lp, options$label), options$customcap,"</p>", sep="")

The entire code would look like:

---
output: 
  bookdown::html_document2:
    self-contained: TRUE
---

```{css, echo=FALSE}
#dualpanel {
  width: 50%
}

@media screen and (max-width: 500px) {
  #dualpanel {
    width: 100%
}}
```

```{r}
knit_hooks$set(customcap= function(before, options, envir) {
  if(!before) {
    paste('<p class="caption"> (\\#fig:chunk1)',options$customcap,"</p>",sep="")
    }
    })
```


```{r chunk1, echo=FALSE, customcap='FIRST FIGURE CAP'}
temp <- plotly::plot_ly(mtcars, x = ~cyl, y=~mpg)
shiny::div(class = 'figure',
  style = "display: flex; flex-wrap: wrap; justify-content: center",
  shiny::div(temp, id = 'dualpanel'), 
  shiny::div(temp, id = 'dualpanel'))
```

```{r chunk2,  echo=FALSE, fig.cap='SECOND FIGURE CAP'}
plot(mtcars$cyl, mtcars$mpg)
```

This produces an output that has the dynamic elements and maintains labeling.

enter image description here

enter image description here