已编辑:见最后的注释
如果您想使用引导主题,可以使用复选框输入和添加/删除 <link> 元素(即加载引导 css 主题的 html 元素)的 javascript 事件来执行此操作。我将shinytheme 切换为darkly,因为有相应的浅色主题(flatly)。我删除了您在 tags$head 中定义的 css,因为它将根据主题切换添加/删除。 (参见下面的完整示例)
即使这样可行,也可能存在性能问题。请注意,每次更改主题时,都会获取文件并将其重新加载到浏览器中。主题之间也存在风格差异,这可能会导致在应用新主题时重新组织或稍微移动内容(这可能会对用户造成干扰)。如果您要选择这种方法,我建议您找到一个设计良好的明暗主题组合。
或者,您可以选择一个基本的引导主题并定义您自己的 css 主题。您可以使用切换开关(如本例)或媒体查询prefers-color-scheme。然后 shinyjs 类函数,您可以从 R 服务器切换主题。通常建议使用这种方法,但开发和验证确实需要更长的时间。
使用引导方法,您可以通过以下方式切换主题。
app.R
在 ui 中,我创建了一个复选框输入并将其放置为最后一个元素(用于示例目的)。
checkboxInput(
inputId = "themeToggle",
label = icon("sun")
)
JS
为了切换引导主题,我定义了 shinythemes 包定义的 html 依赖路径。您可以在您的 R 包库 (library/shinythemes/) 中找到这些。
const themes = {
dark: 'shinythemes/css/darkly.min.css',
light: 'shinythemes/css/flatly.min.css'
}
要加载新主题,需要将路径呈现为 html 元素。我们还需要一个删除现有 css 主题的函数。最简单的方法是选择与href 匹配的元素,如themes 变量中定义的那样。
// create new <link>
function newLink(theme) {
let el = document.createElement('link');
el.setAttribute('rel', 'stylesheet');
el.setAttribute('text', 'text/css');
el.setAttribute('href', theme);
return el;
}
// remove <link> by matching the href attribute
function removeLink(theme) {
let el = document.querySelector(`link[href='${theme}']`)
return el.parentNode.removeChild(el);
}
我还删除了tags$head中定义的样式,并在js中创建了一个新的<style>元素。
// css themes (originally defined in tags$head)
const extraDarkThemeCSS = ".dataTables_length label, .dataTables_filter label, .dataTables_info { color: white!important;} .paginate_button { background: white!important;} thead { color: white;}"
// create new <style> and append css
const extraDarkThemeElement = document.createElement("style");
extraDarkThemeElement.appendChild(document.createTextNode(extraDarkThemeCSS));
// add element to <head>
head.appendChild(extraDarkThemeElement);
最后,我创建了一个事件并将其附加到复选框输入。在此示例中,checked = 'light' 和 unchecked = 'dark'。
toggle.addEventListener('input', function(event) {
// if checked, switch to light theme
if (toggle.checked) {
removeLink(themes.dark);
head.removeChild(extraDarkThemeElement);
head.appendChild(lightTheme);
} else {
// else add darktheme
removeLink(themes.light);
head.appendChild(extraDarkThemeElement)
head.appendChild(darkTheme);
}
})
这是完整的app.R 文件。
library(dplyr)
library(shiny)
library(shinythemes)
ui <- fluidPage(
theme = shinytheme("darkly"),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
checkboxInput(
inputId = "themeToggle",
label = icon("sun")
)
),
tags$script(
"
// define css theme filepaths
const themes = {
dark: 'shinythemes/css/darkly.min.css',
light: 'shinythemes/css/flatly.min.css'
}
// function that creates a new link element
function newLink(theme) {
let el = document.createElement('link');
el.setAttribute('rel', 'stylesheet');
el.setAttribute('text', 'text/css');
el.setAttribute('href', theme);
return el;
}
// function that remove <link> of current theme by href
function removeLink(theme) {
let el = document.querySelector(`link[href='${theme}']`)
return el.parentNode.removeChild(el);
}
// define vars
const darkTheme = newLink(themes.dark);
const lightTheme = newLink(themes.light);
const head = document.getElementsByTagName('head')[0];
const toggle = document.getElementById('themeToggle');
// define extra css and add as default
const extraDarkThemeCSS = '.dataTables_length label, .dataTables_filter label, .dataTables_info { color: white!important;} .paginate_button { background: white!important;} thead { color: white;}'
const extraDarkThemeElement = document.createElement('style');
extraDarkThemeElement.appendChild(document.createTextNode(extraDarkThemeCSS));
head.appendChild(extraDarkThemeElement);
// define event - checked === 'light'
toggle.addEventListener('input', function(event) {
// if checked, switch to light theme
if (toggle.checked) {
removeLink(themes.dark);
head.removeChild(extraDarkThemeElement);
head.appendChild(lightTheme);
} else {
// else add darktheme
removeLink(themes.light);
head.appendChild(extraDarkThemeElement)
head.appendChild(darkTheme);
}
})
"
)
)
server <- function(input, output) {
output$table <- DT::renderDT({
iris
})
}
shinyApp(ui, server)
编辑
在这个例子中,我使用了checkBoxInput。您可以使用以下 css 类“隐藏”输入。我建议添加一个视觉隐藏的文本元素以使该元素可访问。用户界面将更改为以下内容。
checkboxInput(
inputId = "themeToggle",
label = tagList(
tags$span(class = "visually-hidden", "toggle theme"),
tags$span(class = "fa fa-sun", `aria-hidden` = "true")
)
)
然后在css后面添加css。您还可以使用 #themeToggle + span .fa-sun 选择图标并设置其样式
/* styles for toggle and visually hidden */
#themeToggle, .visually-hidden {
position: absolute;
width: 1px;
height: 1px;
clip: rect(0 0 0 0);
clip: rect(0, 0, 0, 0);
overflow: hidden;
}
/* styles for icon */
#themeToggle + span .fa-sun {
font-size: 16pt;
}
这是更新后的用户界面。 (我删除了 js 以使示例更短)
ui <- fluidPage(
theme = shinytheme("darkly"),
tags$head(
tags$style(
"#themeToggle,
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
clip: rect(0 0 0 0);
clip: rect(0, 0, 0, 0);
overflow: hidden;
}",
"#themeToggle + span .fa-sun {
font-size: 16pt;
}"
)
),
mainPanel(
tabsetPanel(
type = "tabs",
tabPanel(
title = "Table",
icon = icon("table"),
tags$br(),
DT::DTOutput("table")
)
),
checkboxInput(
inputId = "themeToggle",
label = tagList(
tags$span(class = "visually-hidden", "toggle theme"),
tags$span(class = "fa fa-sun", `aria-hidden` = "true")
)
)
),
tags$script("...")
)