---
title: 'Shiny App without Shiny: a JavaScript Exercise in R Markdown'
subtitle: Change the number of bins of a histogram with JavaScript
date: '2023-02-07'
slug: js-shiny
---

Please ignore the title of this post. It's an exaggeration. In this post, I want
to show an example that has the feel of a Shiny app, but it doesn't really use
Shiny or R as the backend when it runs. This example is from [an answer of
mine](https://d.cosx.org/d/423312/12) on a forum, and mainly intended for
beginners who are interested in learning a little bit JavaScript, although I'm
not a JavaScript expert. Below is the full R Markdown document:

```` markdown
---
title: "Change the number of bins of a histogram"
---

```{r, setup, echo=FALSE}
n_breaks = c(5, 10, 15, 20)
shiny::selectInput(
    'n_breaks',
    label = 'Number of bins',
    choices = n_breaks,
    selected = 5)
```

Draw all histograms and put them in a container with
the class `hist-all`.

```{r, class.chunk='hist-all'}
x = faithful$eruptions
for (i in n_breaks) {
  hist(x, breaks = seq(min(x), max(x), length.out = i + 1),
       main = paste('n = ', i))
}
```

But we hide all of those images initially with CSS.

```{css}
/* hide all images */
.hist-all img { display: none; }
/* but show the image with the class 'show' */
.hist-all .show { display: block; }
```

When the choice in the select input is changed, show
the histogram with the selected number of bins.

```{js}
(d => {
  const s = d.getElementById('n_breaks'),  // the select input
     imgs = d.querySelectorAll('.hist-all img');  // all histograms
  
  // add the class 'show' to the i-th image, where i is the
  // selected number in the select input
  function showImg() {
    const i = s.selectedIndex;
    imgs.forEach((el, k) => {
      // k is the index of the image; i is the index of the choice
      // in the select input; add the class 'show' if the two
      // indices are equal
      el.classList.toggle('show', k == i); 
    });
  }
  
  // show the initial image
  showImg();
  
  // show a different image when select input is changed
  s.onchange = showImg;
})(document);
```
````

You can knit the document to HTML to play with the select input, or just [visit
my demo](https://rpubs.com/yihui/js-shiny). Personally I liked this example
because it's a combination of my favorite technologies: R, CSS, and JavaScript.
R for drawing plots, CSS for appearance, and JavaScript for interaction. I have
tried to comment the code in detail, and I hope it's not too hard to understand.
The key piece of JavaScript is `el.classList.toggle()`.

This example could be useful when you want to present several plots one by one
on a static HTML page that has no R or Shiny running behind it. But guess what?
Someday you may be able to run real Shiny apps in your web browser without R or
Shiny---[WASM for the win](https://github.com/georgestagg/webR)!
