7 HTML, CSS, and JavaScript
You don't need to know anything about HTML, CSS and JavaScript to make basic Shiny apps, but a little knowledge can really help you customise your apps. This chapter will cover some of the basics so you have enough vocabulary to get started.
7.1 HTML
HTML stands for Hyper-Text Markup Language, a system for semantically tagging structure and information on web pages. The term "semantically" is important here; HTML should tell you what something is, not how to display it (that's handled by CSS). This separation helps your apps be accessible to people who use screen readers.
7.1.2 Viewing HTML
But the main reason for you to learn a little about HTML is that it can help you to customise the appearance of your Shiny apps using CSS and their behaviour using JavaScript.
Often, you'll need to find out how to refer to a specific element or group of elements that were created by a shiny
ui function. For example, it isn't obvious how you'd refer to the sidebar tab for "demo_tab"
in the following code:
sidebarMenu(
id = "tabs",
menuItem("Tab Title", tabName = "demo_tab", icon = icon("dragon"))
)
If you open the resulting app in a web browser, right click on the page, and choose View Page Source
, you'll (eventually) find that the code above created the HTML below.
<ul class="sidebar-menu">
<li>
<a href="#shiny-tab-demo_tab" data-toggle="tab" data-value="demo_tab">
<i class="fa fa-dragon" role="presentation" aria-label="dragon icon"></i>
<span>Tab Title</span>
</a>
</li>
<div id="tabs" class="sidebarMenuSelectedTabItem" data-value="null"></div>
</ul>
Now you know that the dragon icon is made by an italics tag (<i>
) that's inside an anchor (<a>
) with the id
#shiny-tab-demo_tab
. Therefore, you can change the colour of this icon using the following CSS:
#shiny-tab-demo_tab i { color: red; }
It can be tricky to find what code you're looking for, but developer tools can help. I use FireFox Developer Edition when I'm developing web apps, but Chrome also has developer tools. In FireFox, go to Tools > Browser Tools > Web Developer Tools
(opt-cmd-I). In Chrome, go to View > Developer > Developer Tools
(opt-cmd-I). You can dock the tools to the bottom, right , or left of the window, or as a separate window.
Open the Inspector (FireFox) or Elements (Chrome) tab of the tools and click on the icon that looks like an arrow pointing into a box. When you hover over parts of the web page, now you will see boxes outlining each element. You can click on an element to highlight its HTML in the tools.
7.2 CSS
CSS stands for Cascading Style Sheets and is a way to control the visual presentation of HTML on web pages. You can use CSS to change the default appearance of anything on a web page.
7.2.1 CSS Basics
CSS is structured as follows:
; } selector { property: value
Selectors are how you refer to the parts of the HTML you want to style. There are dozens of selectors and they can get very complex. We'll focus on some basic examples below.
Properties are aspects of the visual style that you want to change, such as color
(text color), background-color
, or border
. Values have specific formats for each property, such as "1px"
or "0.5em"
to describe lengths, or red
or #FF0000
to describe colours.
There are hundreds of properties and you can't memorise them all. I usually Google something like "css font type" or "css underline text" and choose the first link from ww3schools.
For example, if you have HTML that looks like this:
<ul class="animals">
<li class="mammal" id="aardvark" >Aardvarks</li>
<li class="insect" id="bee">Bees</li>
<li class="mammal" id="capybara">Capybaras</li>
</ul>
You can refer to different parts of the list in many ways:
CSS | Meaning |
---|---|
.mammal { border: 1px solid green; } |
any element with the class "mammal" will get a 1-pixel green border |
ul.animals { border: 1px solid green; } |
any unordered list with the class "animals" will get a 1-pixel green border |
.animals li { color: blue; } |
any list item element inside an element with the class "animals" will have blue text |
#bee { font-style: italic; } |
the element with the id "bee" will be in italics |
.animals li + li { background-color: grey; } |
inside an element with the class "animals" , any list item that follows another list item will have a grey background |
7.2.2 Styling a single element
You can add styles to most elements that you make with shiny ui functions by adding an argument called style
. You don't need to give them a class
or id
this way, but this is inefficient if you're styling many related elements in the same way.
tags$ul(
style = "width: 10em;",
tags$li("Aardvark"),
tags$li("Bee", style = "background-color: yellow;"),
tags$li("Capybara")
)
7.2.3 Inline CSS
You can add CSS to an app inside the header using the style tag. The code below makes the element with the class
"animal"
"10em"
in width
(em is a unit of size that is proportional to text size). It also makes the element with the id
"bee"
italic and gives it a black background-color
with yellow text color
. If the element is a list item, it makes the marker a bee emoji.
mystyle <- '
.animal { width: 10em; }
#bee {
font-style: italic;
color: yellow;
background-color: black;
}
li#bee::marker { content: "🐝 "; }
'
ui <- fluidPage(tags$head(tags$style(mystyle)),
tags$ul(
class = "animals",
tags$li("Aardvark", class = "mammal", id = "aardvark"),
tags$li("Bee", class = "insect", id = "bee"),
tags$li("Capybara", class = "mammal", id = "capybara")
))
- Aardvarks
- Bees
- Capybaras
7.2.4 External CSS
For anything longer than a few lines, you can see how this can get tedious. You can put all of your CSS in an external file and reference that in the header instead using tags$link()
. The CSS file needs to be inside the www
directory to let Shiny know that it's meant to be included like this. The template we're using in this class comes with a CSS file called www/custom.css
.
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "custom.css")
)
Sometimes when you change external files, they don't seem to update when you test the app. This can be because of caching. You can usually solve this by reloading the app in your web browser, reloading in the web browser with the shift key pressed, stopping the app from running in RStudio with the stop sign icon and starting it up again, and, finally, restarting R.
7.3 JavaScript
JavaScript is a coding language that is very useful for adding dynamic behaviour to web pages. For simple apps, you don't need to understand any JavaScript, but a little bit can be really helpful for adding advanced behaviour.
7.3.1 shinyjs
The R package shinyjs
provides several ways to work with JavaScript in a Shiny app. In order to set it up so that your server function can use shinyjs
function, you need to add shinyjs::useShinyjs()
somewhere in your ui function.
Here is a list of the shinyjs
functions that I find most useful (id
refers to an element with the specified ID):
-
hide(id)
: hide the element -
show(id)
: show the element -
toggle(id)
: change the visibility of the element (hide it if it's visible, and show it if it's not) -
alert(text)
: create an alert popup; this is useful for debugging -
addClass(id, class)
: adds a CSS class to the element -
removeClass(id, class)
: removes a CSS class from the element -
click(id)
: simulates a click on the action button -
disable(id)
: disable an input -
enable(id)
: enables an input -
reset(id)
: resets an input (much easier than the update* functions)
7.3.2 External JS
To do anything more complicated, it's best to put your JavaScript in an external file in the www
directory. You can include a link to this script in the header. The template we're using in this class comes with a JavaScript file called www/custom.js
.
tags$head(
tags$script(src = "custom.js")
)
Shiny apps use jQuery, a framework for making JavaScript easier to write. It lets you refer to elements using their CSS selectors.
Here is some example code from the www/custom.js
file in the basic template.
$(document).on("shiny:connected", function() {
// send window width to shiny
= function() {
shiny_size .setInputValue("window_width", window.innerWidth);
Shiny.setInputValue("window_height", window.innerHeight);
Shiny
}
window.onresize = shiny_size;
shiny_size(); // trigger once at start
})
JavaScript is similar to R in some ways, and maddeningly different in others. One big difference is that lines of code have to end with a semi-colon.
In the code above, the function $(document).on("shiny:connected", function() { ... })
is jQuery shorthand for making sure that the code inside doesn't run until the whole webpage has been downloaded and the extra javascript for shiny is available. Otherwise, you might try to run some code that references an element that hasn't been created yet (HTML pages don't always download all in one go) or uses a Shiny javascript function that isn't available yet.
The we create a new function called shiny_size()
, which creates two new Shiny input variables, "window_width"
and "window_height"
, and sets then to the values of the window dimensions (in pixels). The line window.onresize = shiny_size;
sets this function to run every time the window is resized and the function is run once at the start to initialise those values.
The javaScript function Shiny.setInputValue(input_id, value)
is a way for you to communicate things that happen on the web page to the Shiny app by changing or creating inputs. You can use this inside server()
to, for example, change a plot style if input$window_width < 600
.
7.5 Exercises
Clone the basic template for these exercises.
Add HTML
Add the following HTML before the image in demo_tab
using `HTML()
:
<p class="help">For more help, you can go to <a href="https://shiny.rstudio.com/articles/html-tags.html">Customize your UI with HTML</a>.</p>
Since the HTML has double quotes in it, you either need to escape them or surround the string in single quotes instead.
demo_tab <- tabItem(
tabName = "demo_tab",
HTML(
'<p class="help">For more help, you can go to <a href="https://shiny.rstudio.com/articles/html-tags.html">Customize your UI with HTML</a>.</p>'
),
imageOutput("logo")
)
Class HTML
Add an unordered list under the paragraph that contains the following text and links:
demo_tab <- tabItem(
tabName = "demo_tab",
HTML(
'<p class="help">For more help, you can go to <a href="https://shiny.rstudio.com/articles/html-tags.html">Customize your UI with HTML</a>.</p>'
),
tags$ul(tags$li(
tags$a(href = "https://shiny.rstudio.com/articles/tag-glossary.html", "Tags Glossary")
),
tags$li(
tags$a(href = "https://www.w3schools.com/html/", "HTML Tutorial")
)),
imageOutput("logo")
)
Style links
Change the style of all the links to make them hotpink
. Use the inline CSS method. As a bonus, change the colour of links when you hover over them, too.
tags$head(
tags$style("a { color: hotpink; }
a:hover { color: red; }")
)
Style a single link directly
Change the style of just the first link in the list to make it green
.
tags$ul(
tags$li(tags$a(href = "https://shiny.rstudio.com/articles/tag-glossary.html", "Tags Glossary"), style="color: green;"),
tags$li(tags$a(href = "https://www.w3schools.com/html/", "HTML Tutorial"))
)