I am making a shiny app with a gt table.
I would like to make text in the .gt_group_heading class bold if capitalized.
It seems any text passed to this element is created within the td tag as pure text and not recognized as HTML.
So I was thinking perhaps I can alter the look of the text using Javascript?
When I run the app, I cannot get the script to work.
I get the following error:
Uncaught TypeError: Cannot read property 'innerHTML' of null
at (index):22
However, when I run the app, then type the javascript directly into the browser console, I am able to get a capitalized text to appear as bold.
So is it something about shiny that is preventing the code from running correctly? It seems I need the code to run after the GT table is created, perhaps that is why the error message throws a null - but I don't know how to make that order of events happen in shiny.
FYI I have tried to wrap the strings in df$name with html tags, but this does not render correctly, i.e. the following does not work:
`df$name <- mutate(df$name, paste("<b>", df$name, "</b>"))`
Any help very much appreciated. Here is my sample code.
library(tidyverse)
library(gt)
library(shiny)
df <- tibble(
name = c("john", "john", "jerry", "jerry", "jack", "jack", "jim", "jim"),
day = c("wed", "wed", "thurs", "thurs", "mon", "mon", "tues", "tues"),
lotto = c(12, 42, 24, 57, 234, 556, 34, 23),
car = c("chevy", "toyota", "honda", "gmc", "tesla", "nissan", "ford", "jeep")
) %>%
mutate(name = toupper(name))
options(gt.row_group.sep = "\n")
ui <- fluidPage(
gt_output("table"),
tags$script(
HTML(
"
var heading = document.querySelector('#one > table > tbody > tr > td');
var html = heading.innerHTML;
html = html.replace(/(\b[A-Z]{2,}\b)/g,'<strong>$1</strong>');
heading.innerHTML = html;
"
)
)
)
server <- function(input, output, session){
output$table <- render_gt(
df %>%
arrange(lotto) %>%
gt(
id = "one",
groupname_col = c("name", "day"),
rownames_to_stub = TRUE,
row_group.sep = getOption("gt.row_group.sep", "\n")
) %>%
opt_css(
css = "
#one .gt_group_heading {
white-space:pre-wrap;
word-wrap:break-word;
}
"
)
)
}
shinyApp(ui, server)
A possibility is to delay the execution of the JavaScript code with setTimeout
, for example 2 seconds:
setTimeout(function() {
var heading = document.querySelector('#one > table > tbody > tr > td');
var html = heading.innerHTML;
html = html.replace(/(\b[A-Z]{2,}\b)/g, '<strong>$1</strong>');
heading.innerHTML = html;
}, 2000); // 2000ms = 2s
It's never clear how to chose a nice value of the delay. Another possibility is to use an interval, which is executed every 100ms until it finds the object:
var myinterval = setInterval(function() {
var heading = document.querySelectorAll('#one > table > tbody > tr > td');
if(heading) {
clearInterval(myinterval);
for(var i=0; i<heading.length; i++){
var html = heading[i].innerHTML;
html = html.replace(/(\b[A-Z]{2,}\b)/g, '<strong>$1</strong>');
heading[i].innerHTML = html;
}
}
}, 100);
This is the JavaScript code. But since it is sent from a string, you have to double the backslashes in the regular expression: /(\\b[A-Z]{2,}\\b)/g
.