I'm using the groupname_col
option of the gt
package to group the table. When we use this in combination with the opt_interactive
function we can collapse the tables per group which is really usefull. Initially the folds are open, but I want to have them closed at start. Here is some reproducible code:
library(gt)
iris |>
gt(groupname_col = "Species") |>
opt_interactive()
Output:
As you can see the setosa tab is initially open. How can we have these tabs all initially closed?
There doesn't seem to be a way to do this directly by providing an R option. However, if you look at the html file generated you'll see that it contains some json with "defaultExpanded":true
. There are two ways to fix this:
defaultExpanded
value before saving to htmlWe can basically reproduce what gt:::gt_save_html()
does under the hood, but set defaultExpanded <- TRUE
:
create_collapsed_html <- function(gt_tbl, outfile = "gt.html") {
gt_tbl <- gt:::as.tags.gt_tbl(gt_tbl)
gt_tbl$children <- lapply(
gt_tbl$children,
\(l) {
target <- l$x$tag$attribs$defaultExpanded
if (!is.null(target)) {
l$x$tag$attribs$defaultExpanded <- FALSE
}
l
}
)
htmltools::save_html(gt_tbl, outfile)
}
iris |>
gt(groupname_col = "Species") |>
opt_interactive() |>
create_collapsed_html()
This will create an html file with the sections not expanded by default:
If you're not writing to a static html file, e.g. you are creating a Shiny app, the above might not be an option. You can instead use some JS to click the button to collapse each of the sections when the page loads. Having said that, this is more challenging than I thought. There are a few hurdles:
gt
table with htmlwidgets::saveWidget()
so you can't inject JS with htmlwidgets::onRender()
.Here is some JS which basically waits for the buttons to load, counts how many there are, collapses them all and then stops listening:
function collapseAllButtons() {
function collapseButton(button) {
button.click();
buttonsCollapsed += 1;
if (buttonsCollapsed < numButtons) {
// keep going until none left
requestAnimationFrame(waitForButtons);
}
}
function waitForButtons(timestamp, buttonSelector = '.rt-expander-button[aria-expanded=\"true\"]') {
const button = document.querySelector(buttonSelector);
if (button) {
if (firstRun) {
numButtons = document.querySelectorAll(buttonSelector).length;
firstRun = false;
}
collapseButton(button); // begin collapsing
} else {
requestAnimationFrame(waitForButtons); // check again next frame
}
}
let firstRun = true;
let buttonsCollapsed = 0;
let numButtons;
requestAnimationFrame(waitForButtons);
}
collapseAllButtons();
If we save this as collapseAllButtons.js
, in R we can then append this in a <script>
to the html generated by gt
:
script <- sprintf(
"<script>%s\n</script>",
paste(readLines("./collapseAllButtons.js"), collapse = "\n")
) |> htmltools::HTML()
iris |>
gt(groupname_col = "Species") |>
opt_interactive() |>
gt:::as.tags.gt_tbl() |>
htmltools::tagAppendChildren(script) |>
htmltools::save_html("./gt_iris_tmp.html")
Or in Shiny you can just stick the script in there somewhere. For a Quarto example, see below.
Here is a minimal example of how to do use the first approach in Quarto. This is a complete qmd
file:
# Iris example
```{r, echo=FALSE}
create_collapsed_html <- function(gt_tbl, outfile = "gt.html") {
gt_tbl <- gt:::as.tags.gt_tbl(gt_tbl)
gt_tbl$children <- lapply(
gt_tbl$children,
\(l) {
target <- l$x$tag$attribs$defaultExpanded
if (!is.null(target)) {
l$x$tag$attribs$defaultExpanded <- FALSE
}
l
}
)
gt_tbl
}
```
```{r}
iris |>
gt::gt(groupname_col = "Species") |>
gt::opt_interactive() |>
create_collapsed_html()
```
If you quarto render
this you should get: