shinyjs
lets you perform common useful JavaScript operations in Shiny
applications without having to know any JavaScript. Examples include:
hiding an element, disabling an input, resetting an input back to its
original value, delaying code execution by a few seconds, and many more
useful functions. shinyjs
can also be used to easily run your own
custom JavaScript functions from R.
As an extra perk, shinyjs
also comes with a colourInput()
Shiny
widget, and with a colour picker gadget+RStudio addin that let you
choose colours easily.
- Demos
- Installation
- Overview of main functions
- How to use
- Basic use case - complete working example
- Calling your own JavaScript functions from R
- FAQ and extra tricks
- Colour Picker input & addin
shinyjs
also includes a colourInput
which is an input control that
allows users to select colours. Interactive
demo.
You can also watch a short GIF showcasing the colour picker addin+gadget.
Presentation slides for shinyjs
talk
from the 2016 Shiny Developer Conference.
To install the stable CRAN version:
install.packages("shinyjs")
To install the latest development version from GitHub:
install.packages("devtools")
devtools::install_github("daattali/shinyjs")
-
show
/hide
/toggle
- display or hide an element. There are arguments that control the animation as well, though animation is off by default. -
hidden
- initialize a Shiny tag as invisible (can be shown later with a call toshow
). -
reset
- reset a Shiny input widget back to its original value. -
enable
/disable
/toggleState
- enable or disable an input element, such as a button or a text input. -
delay
- execute R code (including anyshinyjs
functions) after a specified amount of time. -
disabled
- initialize a Shiny input as disabled. -
info
- show a message to the user (using JavaScript'salert
under the hood). -
html
- change the text/HTML of an element (using JavaScript'sinnerHTML
under the hood). -
onclick
- run R code when an element is clicked. Was originally developed with the sole purpose of running ashinyjs
function when an element is clicked, though any R code can be used. -
onevent
- similar toonclick
, but can be used with many other events instead of click. -
addClass
/removeClass
/toggleClass
- add or remove a CSS class from an element -
inlineCSS
- easily add inline CSS to a Shiny app. -
logjs
- print a message to the JavaScript console (mainly used for debugging purposes). -
runjs
- run arbitrary JavaScript code (not recommended to use this in a published Shiny app). -
extendShinyjs
- allows you to write your own JavaScript functions and useshinyjs
to call them as if they were regular R code. More information is available in the section "Calling your own JavaScript functions from R". -
colourInput
andupdateColourInput
- input widget that allows users to select colours. -
colourPicker
- opens an RStudio gadget that lets you select colours (you can also call it from the RStudio Addins menu).
Check out the demo Shiny app
to see some of these in action, or install shinyjs
and run
shinyjs::runExample()
to see more demo apps.
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
actionButton("button", "Click me"),
div(id = "hello", "Hello!")
)
server <- function(input, output) {
observeEvent(input$button, {
toggle("hello")
})
}
shinyApp(ui, server)
This is how most Shiny apps should initialize shinyjs
, but there a few
scenarios that should be treated a little differently: using shinyjs
in Shiny dashboards, in Shiny apps that use a navbarPage
layout, in
interactive Rmd documents, or in Shiny apps that manually build the user
interface with an HTML file or template instead of using Shiny's UI
functions. If your Shiny app doesn't fall into any of these categories,
then you can skip the next 4 sub-sections that describe how to tackle
these cases, and scroll down to the Basic use case section.
library(shiny)
library(shinydashboard)
library(shinyjs)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
useShinyjs(),
actionButton("button", "Click me"),
div(id = "hello", "Hello!")
)
)
server <- function(input, output) {
observeEvent(input$button, {
toggle("hello")
})
}
shinyApp(ui, server)
library(shiny)
library(shinyjs)
ui <- tagList(
useShinyjs(),
navbarPage(
"shinyjs with navbarPage",
tabPanel("tab1",
actionButton("button", "Click me"),
div(id = "hello", "Hello!")),
tabPanel("tab2")
)
)
server <- function(input, output, session) {
observeEvent(input$button, {
toggle("hello")
})
}
shinyApp(ui, server)
library(shinyjs)
useShinyjs(rmd = TRUE)
actionButton("button", "Click me")
div(id = "hello", "Hello!")
observeEvent(input$button, {
toggle("hello")
})
A similar way to create your app's UI with HTML is to write it entirely
in HTML (without templates), as RStudio shows in this
article. Building Shiny
apps like this is much more complicated and should only be used if
you're very comfortable with HTML. Using shinyjs
in these apps is
possible but it works a little differently since there is no ui.R
to
call useShinyjs()
from. There are three simple steps to take in order
to use shinyjs
in these apps:
-
create a
global.R
file in the same directory as yourserver.R
, and add the following line to the file:shiny::addResourcePath("shinyjs", system.file("srcjs", package = "shinyjs"))
-
in the
index.html
file you need to load a special JavaScript file namedshinyjs/inject.js
. You do this by adding the following line to the HTML's<head>
tag:`<script src="shinyjs/inject.js"></script>`
-
in your server function (the
shinyServer
function) you need to calluseShinyjs(html = TRUE)
After adding these three lines to your code, you can use all shinyjs
functions as usual.
Suppose we want to have a simple Shiny app that collects a user's basic information (name, age, company) and submits it, along with the time of submission. Here is a very simple implementation of such an app (nothing actually happens when the user "submits").
library(shiny)
shinyApp(
ui = fluidPage(
div(id = "myapp",
h2("shinyjs demo"),
textInput("name", "Name", ""),
numericInput("age", "Age", 30),
textInput("company", "Company", ""),
p("Timestamp: ", span(date())),
actionButton("submit", "Submit")
)
),
server = function(input, output) {
}
)
Note that I generally don't like running Shiny apps like this and prefer to declare the UI and server separately, but this style is used here for brevity.
Here is what that app would look like
Now suppose we want to add a few features to the app to make it a bit
more user-friendly. First we need to set up the app to use shinyjs
by
making a call to useShinyjs()
in the Shiny app's UI.
Here are 7 features we'll add to the app, each followed with the code to
implement it using shinyjs
:
1. The "Name" field is mandatory and thus the "Submit" button should not be enabled if there is no name
In the server portion, add the following code
observe({
if (is.null(input$name) || input$name == "") {
shinyjs::disable("submit")
} else {
shinyjs::enable("submit")
}
})
Or instead you can use the toggleState
function and pass it a
condition
:
observe({
shinyjs::toggleState("submit", !is.null(input$name) && input$name != "")
})
You can use the optional condition
in some other functions as well,
which can be very useful to make your code shorter and more
understandable.
2. The "Age" and "Company" fields are optional and we want to have the ability to hide that section of the form
First, we need to section off the "Age" and "Company" elements into
their own section, so we surround them with a div
div(id = "advanced",
numericInput("age", "Age", 30),
textInput("company", "Company", "")
)
We also need to add a link in the UI that will be used to hide/show the section
a(id = "toggleAdvanced", "Show/hide advanced info")
Lastly, we need to tell Shiny to show/hide the section when the link is clicked by adding this code to the server
shinyjs::onclick("toggleAdvanced",
shinyjs::toggle(id = "advanced", anim = TRUE))
3. Similarly, since we don't really care about "Age" and "Company" too much, we want to hide them initially when the form loads
Simply surround the section we want to hide initially with
shinyjs::hidden
shinyjs::hidden(
div(id = "advanced",
...
))
4. The user should be able to update the "Timestamp" in case he spends way too long filling out the form (not very realistic here, and the timestamp should ideally be determined when the button is clicked, but it's good enough for illustration purposes)
First, we need to add an "Update" link to click on, and we need to give
the element showing the time an id
so that we can refer to it later
when we want to change its contents.
To do that, replace p("Timestamp: ", span(date()))
with
p("Timestamp: ", span(id = "time", date()), a(id = "update", "Update"))
Now we need to tell Shiny what to do when "Update" is clicked by adding this to the server
shinyjs::onclick("update", shinyjs::html("time", date()))
5. Some users may find it hard to read the small text in the app, so there should be an option to increase the font size
First, we need to add checkbox to the UI
checkboxInput("big", "Bigger text", FALSE)
In order to make the text bigger, we will use CSS. So let's add an appropriate CSS rule by adding this code to the UI
shinyjs::inlineCSS(list(.big = "font-size: 2em"))
Lastly, we want the text to be big or small depending on whether the checkbox is checked by adding this code to the server
observe({
if (input$big) {
shinyjs::addClass("myapp", "big")
} else {
shinyjs::removeClass("myapp", "big")
}
})
Or, again, we can use the toggleClass
function with the condition
argument:
observe({
shinyjs::toggleClass("myapp", "big", input$big)
})
6. Give the user a "Thank you" message upon submission
Simply add the following to the server
observeEvent(input$submit, {
shinyjs::info("Thank you!")
})
7. Allow the user to reset the form
First let's add a button to the UI
actionButton("reset", "Reset form")
And when the button is clicked, reset the form
observeEvent(input$reset, {
shinyjs::reset("myapp")
})
The final code looks like this
library(shiny)
shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
shinyjs::inlineCSS(list(.big = "font-size: 2em")),
div(id = "myapp",
h2("shinyjs demo"),
checkboxInput("big", "Bigger text", FALSE),
textInput("name", "Name", ""),
a(id = "toggleAdvanced", "Show/hide advanced info", href = "#"),
shinyjs::hidden(
div(id = "advanced",
numericInput("age", "Age", 30),
textInput("company", "Company", "")
)
),
p("Timestamp: ",
span(id = "time", date()),
a(id = "update", "Update", href = "#")
),
actionButton("submit", "Submit"),
actionButton("reset", "Reset form")
)
),
server = function(input, output) {
observe({
shinyjs::toggleState("submit", !is.null(input$name) && input$name != "")
})
shinyjs::onclick("toggleAdvanced",
shinyjs::toggle(id = "advanced", anim = TRUE))
shinyjs::onclick("update", shinyjs::html("time", date()))
observe({
shinyjs::toggleClass("myapp", "big", input$big)
})
observeEvent(input$submit, {
shinyjs::info("Thank you!")
})
observeEvent(input$reset, {
shinyjs::reset("myapp")
})
}
)
You can view the final app here.
You can also use `shinyjs` to add your own JavaScript functions that can be called from R as if they were regular R functions using `extendShinyjs`. Note that you have to install the `V8` package in order to use this function. Using `extendShinyjs` is very simple and makes defining and calling JavaScript functions painless. Here is a very basic example of using `extendShinyjs` to define a (fairly useless) function that changes the colour of the page.library(shiny)
library(shinyjs)
jsCode <- "shinyjs.pageCol = function(params){$('body').css('background', params);}"
shinyApp(
ui = fluidPage(
useShinyjs(),
extendShinyjs(text = jsCode),
selectInput("col", "Colour:",
c("white", "yellow", "red", "blue", "purple"))
),
server = function(input, output) {
observeEvent(input$col, {
js$pageCol(input$col)
})
}
)
Running the code above produces this shiny app:
See how easy that was? All I had to do was make the JavaScript function
shinyjs.pageCol
, pass the JavaScript code as an argument to
extendShinyjs
, and then I can call js$pageCol()
. That's the basic
idea: any JavaScript function named shinyjs.foo
will be available to
call as js$foo()
. You can either pass the JS code as a string to the
text
argument, or place the JS code in a separate JavaScript file and
use the script
argument to specify where the code can be found. Using
a separate file is generally prefered over writing the code inline, but
in these examples I will always use the text
argument to keep it
simple.
For example, the following example uses shinyjs.init
to register an
event handler so that every keypress will print its corresponding key
code:
jscode <- "
shinyjs.init = function() {
$(document).keypress(function(e) { alert('Key pressed: ' + e.which); });
}"
shinyApp(
ui = fluidPage(
useShinyjs(),
extendShinyjs(text = jscode),
"Press any key"
),
server = function(input, output) {}
)
To assist in normalizing the parameters, shinyjs
provides a
shinyjs.getParams()
function which serves two purposes. First of all,
it ensures that all arguments are named (even if the R function was
called without names). Secondly, it allows you to define default values
for arguments. Here is an example of a JS function that changes the
background colour of an element and uses shinyjs.getParams()
.
shinyjs.backgroundCol = function(params) {
var defaultParams = {
id : null,
col : "red"
};
params = shinyjs.getParams(params, defaultParams);
var el = $("#" + params.id);
el.css("background-color", params.col);
}
Note the defaultParams
that we defined and the call to
shinyjs.getParams
. It ensures that calling
js$backgroundCol("test", "blue")
and
js$backgroundCol(id = "test", col = "blue")
and
js$backgroundCol(col = "blue", id = "test")
are all equivalent, and
that if the colour parameter is not provided then "red" will be the
default. All the functions provided in shinyjs
make use of
shinyjs.getParams
, and it is highly recommended to always use it in
your functions as well. Notice that the order of the arguments in
defaultParams
in the JavaScript function matches the order of the
arguments when calling the function in R with unnamed arguments. This
means that js$backgroundCol("blue", "test")
will not work because the
arguments are unnamed and the JS function expects the id to come before
the colour.
For completeness, here is the code for a shiny app that uses the above
function (it's not a very practical example, but it's great for showing
how to use extendShinyjs
with parameters):
library(shiny)
library(shinyjs)
jsCode <- '
shinyjs.backgroundCol = function(params) {
var defaultParams = {
id : null,
col : "red"
};
params = shinyjs.getParams(params, defaultParams);
var el = $("#" + params.id);
el.css("background-color", params.col);
}'
shinyApp(
ui = fluidPage(
useShinyjs(),
extendShinyjs(text = jsCode),
p(id = "name", "My name is Dean"),
p(id = "sport", "I like soccer"),
selectInput("col", "Colour:",
c("white", "yellow", "red", "blue", "purple")),
textInput("selector", "Element", ""),
actionButton("btn", "Go")
),
server = function(input, output) {
observeEvent(input$btn, {
js$backgroundCol(input$selector, input$col)
})
}
)
And the resulting app:
Note that I chose to define the JS code as a string for illustration
purposes, but in reality I would prefer to place the code in a separate
file and use the script
argument instead of text
.
If you are deploying an app that uses extendShinyjs
to
shinyapps.io then you need to add a call
to library(V8)
somewhere in your code. This is necessary because the
shinyapps.io server needs to know that it should install the V8
package. If you do not do this then you will simply see an error saying
the package is missing.
If you cannot install the V8
package on your machine (some very old
operating systems don't support it), then you can still use
extendShinyjs()
but you will have to provide the functions
argument.
Read more about this argument with ?shinyjs::extendShinyjs
.
- How do I show/hide the
shinydashboard
sidebar programmatically? - How do I hide/disable a tab?
- How do I refresh the page?
- How do I call a JavaScript function from a different JavaScript library?
- How do I change the values of a
sliderInput
? - How do I call JavaScript code and use the return value?
The initial release of this package was announced on my blog and discusses these topics.
If you have any suggestions or feedback, I would love to hear about it. You can either message me directly, open an issue if you want to request a feature/report a bug, or make a pull request if you can contribute. I'd like to give special thanks to the Shiny developers, especially Joe Cheng for always answering all my Shiny questions.