10 JavaScript for Shiny
Designing an outstanding interface is not just about making it look nice with HTML and CSS. How do we handle interactivity, widget creation and data flow between R and JS? This is where JavaScript (JS) is our biggest ally. To understand how Shiny works from inside, especially how inputs are handled, we gradually dive into its core, which contains a substantial amount of JS. Therefore, this chapter proposes a rather brief introduction to JS and jQuery, but this is still necessary as this book is supposed to be standalone. Advanced JS users may likely skip this part. If you wish to know more about this language, MDN web docs by Mozilla is an excellent resource.
10.1 Shiny JavaScript sources
Since commit 1b8635d (https://github.com/rstudio/shiny/commit/1b8635db327a753007b1d7d052b138353745d251), the whole JS core has been converted to TypeScript. As this book was written before these changes, we’ll point the user to the code prior to 1b8635d, that is this state (https://github.com/rstudio/shiny/tree/60db1e02b03d8e6fb146c9bb1bbfbce269231add). Practically, the underlying mechanisms remain exactly the same.
Let’s have a look at the shiny (Chang et al. 2021) github project. As a R package, it is composed of standard folders like R
, man
, tests
and other elements. The inst
folder contains resources for external dependencies like Bootstrap 3, jQuery, datatables, Font Awesome, … mentioned in Chapter 3 sorted in the www/shared
sub-folder, as well as the whole CSS and JS Shiny codes. Notice the presence of minified files like shiny.min.js
and non-minified elements such as shiny.css
. Overall, the minification process reduces the loading time of a web page by removing comments and extra spaces, thereby decreasing the file size. For instance shiny.js
has more than 6500 lines of code (240kb), while shiny.min.js
is only 91.4kb.
Notice the srcjs/
folder shown in Figure 10.1. It actually contains all pieces to reconstruct the whole shiny.js
file.
Since in Chapter 12 we’ll use some of those scripts, a little understanding of the basic underlying JavaScript concepts is necessary.
10.2 Introduction to JavaScript
JavaScript was created in 1995 by Brendan Eich and is also known as ECMAScript (ES). Interestingly, you might have heard about ActionScript, which is no more than an implementation of ES by Adobe Systems. Nowadays, JavaScript is the centerpiece of web development across all websites.
Here is a quick example. If you have a personal blog, you probably know Hugo or Jekyll, especially the R interfaces like blogdown (Xie, Dervieux, and Presmanes Hill 2021). These tools allow one to rapidly develop a nice-looking blog in just a few minutes, focusing on the content rather than technical aspects, which is really the point! Now, if you open the HTML inspector introduced in Chapter 1.3, click on the elements tab, which may open by default, and uncollapse the <head>
tag, you see that a lot of scripts are included, as shown in Figure 10.2. This is similar for the <body>
tag.
There are three ways to include scripts in an HTML document:
- Use the
<script>
tag with the JS code inside. - Add the
onclick
attribute to an HTML tag (preferably a button) to trigger JS as soon as it is clicked. - Import an external file containing the JS code only.
<script type="text/javascript">
// JS code here
</script>
<button id="hello" onclick="jsFunction()">Hello World</button>
<!-- We use the src attribute to link the external file -->
<script type="text/javascript" src="file.js">
Whether to choose the first, second or third method depends on the content of your script. If we consider the JS library jQuery, it unfortunately contains so much code making it a challenge to understand. This makes user choose the third method most of the time.
10.3 Setup
Like R or Python, JavaScript (JS) is an interpreted language. It is executed client-side, in other words in the browser. This also means that JS code may not be run without a suitable tool. In the following, we’ll list some tools to test JS code, even though JS may also be run through the web browser developer tools, as demonstrated in section 10.6.
10.3.1 Node
Node contains an interpreter for JS as well as a dependencies manager, npm (Node Package Manager). To install Node on your computer, browse to the website and follow the installation instructions. Afterwards, open a terminal and check if:
which node
node --version
returns something. If not, Node may not be properly installed.
If you prefer not installing Node, there exists alternatives like repl.it (https://repl.it/languages/nodejs), offering a Node.js online compiler environment. This will be more than enough to follow this part.
10.3.2 Choose a good IDE
Personally, I really like VSCode for coding with JS, as it is chipped with a plug and play Node interpreter, allowing you to seamlessly execute any JS code. Moreover, VSCode supports R very well, a comprehensive installation guide being shown here. In the remainder of the book, most screenshots are taken from RStudio since, at the time of writing, this is still the best IDE choice for R users. However, keep in mind that VSCode is even more powerful for web development, particularly to handle JSX code, as studied in Chapters 27 and 28. As a side note, I encourage you to try the dracula color theme, which is my favorite. RStudio IDE can also handle JS code, but running it is less straightforward. Below, we will explain how to run a JS code in both IDE’s. In section 10.6, we will show how to manipulate JS code directly in the web browser, through the HTML inspector. This is the method we will mostly rely on in the remainder of the book since we will also work with HTML and CSS at the same time.
10.3.3 First script
Let’s write our first script:
console.log('Hello World');
You notice that all instructions end with a ;
. You can run this script either in RStudio IDE or VSCode.
In VSCode, clicking on the run arrow (top center) of Figure 10.3 triggers the node hello.js
command, which tells Node to run the script. We see the result in the right panel (code=0 means the execution is fine, and we even have the compute time). To run this script in the RStudio IDE, you need to click on the terminal tab (you could also open a basic terminal) and type node hello.js
(or node mycustompath/hello.js
if you are not already in the script folder). You should see the Hello World
message in the console (see Figure 10.4).
10.4 Programming with JS: basis
We are now all set to introduce the basis of JS. As in many languages, JS is made of variables and instructions. All instructions end with a ;
symbol.
10.4.1 JS types
JS defines several types:
- Number: JS does not distinguish between integers and others. In R for instance, numeric contains integers and double.
- String: characters (‘blabla’).
- Boolean: true/false.
To check the type of an element, we may use the typeof
operator:
typeof 1; // number
typeof 'pouic'; // string
In JS, typeof
is not a function like in R! Therefore, don’t write typeof('string');
.
10.4.2 Variables
Variables are key elements to programming languages. They allow you to store intermediate results and do other manipulations. In JS, a variable is defined by:
- A type.
- A name.
- A value.
A valid variable name:
- Doesn’t use a reserved JS name like
typeof
! - Doesn’t start with a number (123soleil)!
- Doesn’t include any space (total price)!
Besides, code style is a critical element in programming, increasing readability and general consistency. There are several styles, the main ones being snake_case and camelCase. As shown in the following, there are two ways to create variables in JavaScript.
10.4.2.1 Const
We may use const:
const n = 1;
= 2; // error
n const n = 3; // error
const a;
= 1; // errors a
As shown above, such variables:
- Cannot be modified.
- Cannot share the same name.
- Must be assigned a value.
10.4.2.2 let
Another way to define a variable:
let myVariable = 'welcome';
= 1;
myVariable console.log(myVariable);
All mathematical operators apply, for example:
let myNumber = 1; // initialize
--; // decrement
myNumberconsole.log(myNumber); // print 0
List of numerical operators in JS:
You may also know var
to declare variables. What is the difference with let
? It is mainly a scope reason:
var i = 1;
{var i = 2; // this will modify i globally, not locally
}console.log(`i is ${i}`); // i is 2.
let j = 1;
{let j = 2; // j is only declared locally and not globally!
}console.log(`j is ${j}`); // j is 1
You will see later that we still use var
in the Shiny core.
10.4.3 Conditions
Below are the operators to check conditions:
Importantly, prefer ===
and !==
to compare elements since 5 == "5"
would return true
, which is generally not what you want!
There are several ways to test conditions:
if (condition) { console.log('Test passed'); }
if (condition) { instruction A } else { instruction B }
The ternary operator is a shortcut condition ? instruction if true : instruction if false
that may be chained. For complex instructions, we recommend not using it, as it may affect code readability.
Whenever a lot of possible conditions have to be evaluated, it is better to choose the switch
:
switch (variable) {
case val1: // instruction 1
break; // don't forget the break!
case val2: // instruction 2
break;
default: // when none of val1 and val2 are satisfied
}
10.4.4 Objects
JavaScript is an object-oriented programming language (like Python). An object is defined by:
- A type.
- Some properties.
- Some methods (to manipulate properties).
Let’s construct our first object:
const me = {
name : 'Divad',
age : 29,
music : '',
printName: function() {
console.log(`I am ${this.name}`);
}
}
.geek = true; // works (see const variables above)
me// print a human readable object.
console.log(JSON.stringify(me));
console.log(me.name);
console.log(me.age);
console.log(me.music);
// don't repeat yourself!!!
for (let key in me) { // here is it ok to use `in`
console.log(`me[${key}] is ${me[key]}`);
}
.printName();
me
= {
me name: 'Paul',
age: 40
// error (see const variables above) }
Some comments on the above code:
- To access an object property, we use
object.<property>
. - To print a human readable version of the object,
JSON.stringify
will do the job. - We introduced string interpolation with
${*}
.*
may be any valid expression. - Methods are called with
object.<method>
. We usethis
to refer to the object itself. Take note, we will see it a lot!
In JavaScript, there are already predefined objects to interact with arrays, dates, …
10.4.4.1 Arrays
An array is a structure allowing you to store information; for instance:
const table = [1, 'plop'];
= [2]; // error
table console.log(table);
Array may be nested:
const nested = [1, ['a', [1, 2, 3]], 'plop'];
console.log(nested);
In arrays, elements may be accessed by their index, but as mentioned before, the first index is 0 (not 1 like in R).
For instance, if we want to get the first element of the nested
array, we do:
console.log(nested[0]);
// To get deeply nested element
// we may chain index
1][1] // Access [1, 2, 3] nested[
Note that the length
method returns the size of an array and is very convenient in for loops, as we’ll see later:
.length // 3
nested1].length // 2
nested[1][1].length // 3 nested[
Below is a table referencing the principal methods for arrays.
Method/Property | Description |
---|---|
length | Return the number of elements in an array |
join(separator) | Transform an array into a single string |
concat(array1, array2) | Assemble 2 arrays |
pop() | Remove the last element of an array |
shift() | Remove the first element of an array |
unshift(el1, el2, …) | Insert elements at the beginning of an array |
push(el1, el2, …) | Add extra elements at the end of an array |
sort() | Sort array elements by increasing value of alphabetical order |
reverse() | Symetric of sort() |
Among those methods, we mainly use push
and length
in the next chapters:
.push('hello'); // [1, 'plop', 'hello']; table
10.4.4.2 Strings
Below are the main methods related to the String object (character in R).
Method/Property/Operator | Description |
---|---|
+ (operator) | String concatenation |
length | String length |
indexOf() | Position of the character following the input string |
toLowerCase() | Put the string in small letters |
toUpperCase() | Put the string in capital letters |
10.4.4.3 Math
Below we mention some useful methods to handle mathematical objects.
Method | Description |
---|---|
parseInt(string, radix) | Convert a string to integer |
parseFloat() | Conversion to floating number |
All classic functions such as trigonometric functions are, of course, available. We call them with the Math.*
prefix.
10.4.5 Iterations
Iterations allow you to repeat an instruction or a set of instructions multiple times. Let’s assume we have an array containing 100,000 random numbers. How would you automatically print them? This is what we are going to see below!
10.4.5.1 For loops
The for loop has multiple uses. A convenient way to print all an array’s elements is to use an iteration:
// ES6 syntax
const nested = [1, ['a', [1, 2, 3]], 'plop'];
for (let i of nested) {
console.log(i);
}
// or with the classic approach
for (let i = 0; i < nested.length; i++) {
console.log(nested[i]);
}
The modern approach (ES6) consists in the for
, let
and of
keywords. We define the variable
i
that will take all values among the nested
array and print them. Importantly, i
does not refer
to the element index, but the element itself! The classic approach shown below uses the index
, being slightly more
verbose.
Contrary to R, the JavaScript index starts from 0 (not from 1)! This is good to keep in mind when we mix both R and JS.
JS has other methods to do iterations. Let’s have a look at the forEach
method for arrays (introduced in ES5):
const letters = ['a', 'b', 'c', 'd'];
.forEach((letter) => {
lettersconsole.log(letter);
; })
Since arrays are objects, it is not surprising to see array.forEach(element, ...)
.
Which for
loop should we use? The answer is: it depends on the situation! Actually, there even exists other ways (replace of
by in
and you get the indexes of the array, like with the first code, but this is not recommended).
10.4.6 Functions
Functions are useful to wrap a succession of instructions to accomplish a given task. Defining functions allows programmers to save time (less copy and paste, less search and replace), make less errors and easily share code. In modern JavaScript (ES6), functions are defined as follows:
const a = 1;
const fun = (parm1, parm2) => {
console.log(a);
let p = 3;
// The Math object contains the max method
return Math.max(parm1, parm2);
}let res = fun(1, 2);
console.log(res); // prints a and 2
console.log(p); // fails as p was defined inside the function
The above function computes the maximum of two provided numbers. Some comments about scoping rules: variables defined inside the function, like p
, are only available within the function scope, but not outside. It should be noted that functions may use global variables, defined outside like a
. In the Shiny JS core, you’ll still find the classic way of defining function:
function initShiny() {
// do things
}
The main difference being that the ES6 syntax may not be understood by all environments.
10.4.6.1 Export functions: about modules
What happens if you write 100 functions that you want to reuse in different scripts? To prevent copying and pasting, we will now introduce the concept of modules. Let’s save the below function in a script utils.js
:
const findMax = (parm1, parm2) => {
return Math.max(parm1, parm2);
}
.exports = {
modulefindMax : findMax
}
We create a test.js
script in the same folder that calls the findMax
function. To do this, we import the corresponding module:
const { findMax } = require('./utils.js');
findMax(1, 2); // prints 2
ES6 introduced another way to import and export elements across multiple scripts, which will be leveraged starting from Chapter 23.
export { findMax, ... }; // in utils.js
import { findMax, ...} from './utils.js'; // in test.js
10.4.7 JS code compatibility
As ES6 is not fully supported by all web browsers, you might wonder how to make your JS code standard. A tool called transpiler can convert any modern JS code into a universal JS code. This is the case of Babel, one of the most commonly used. In Chapter 21, we’ll see how to do it with a JS bundler, namely esbuild
, as well as webpack in Chapters 27 and
28.
10.4.8 Event listeners
When you explore a web application, clicking on a button usually triggers something like a computation, a modal or an alert. How does this work? In JavaScript, interactivity plays a critical role. Indeed, you want the web application to react to user inputs like mouse clicks or keyboard events. Below we introduce DOM events.
Let’s consider a basic HTML button:
<button id="mybutton">Go!</button>
On the JavaScript side, we first capture the button element using its id
selector with getElementById
:
const btn = document.getElementById('mybutton');
We then apply the addEventListener
method. In short, an event listener is a program that triggers when a given event occurs (we can add multiple event listeners per HTML element). It takes two main parameters:
- The event: click, change, mouseover, …
- The function to call, also known as callback.
.addEventListener('click', function() {
btnalert('Thanks!');
; })
10.5 jQuery
10.5.1 Introduction
jQuery is a famous JavaScript library providing a user-friendly interface to manipulate the DOM and is present in almost all actual websites. It is slightly easier (more convenient to use) than vanilla JS. To use jQuery in a web page, we must import its code in the head
of our HTML page:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Including jQuery</title>
<!-- How to include jQuery -->
<script src="https://code.jquery.com/jquery-3.5.1.js">
</script>
</head>
<body>
<p>Hello World</p>
<script>
$('p').css('color', 'red');
</script>
</body>
</html>
10.5.2 Syntax
The following is a minimal jQuery code representing its philosophy (“write less, do more.”):
$(selector).action();
The selector slot stands for any jQuery selector like class
, id
, element
, [attribute]
, :input
(will select all <input>
elements) and many more. As a reminder, let’s consider the following example:
<p class="text">Hello World</p>
To select and interact with this element, we use JavaScript and jQuery:
// vanilla JS
let inner = document.getElementsByClassName('text').innerHTML;
// jQuery
let inner = $('.text').html();
This is, of course, possible to chain selectors:
<ul class="list">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item" id="precious-item">4</li>
</ul>
<ul class="list" id="list2">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
</ul>
// Returns an array containing 8 li tags
let items = $('.list .item');
// Selects only li tags from the second ul element
let otherItems = $('#list2 .item');
// Returns an array with 2 ul elements
let lists = $('ul');
// Returns the first li element of the second ul.
let firstItem = $('#list2:first-child');
The good news is that you should already be at ease with CSS selectors from section 6.2.
10.5.3 Good practice
It is recommended to wrap any jQuery code as follows:
$(document).ready(function(){
// your code
;
})
// or a shortcut
$(function() {
// your code
; })
This is to avoid interacting with the DOM before it is actually ready. Most of the time,
if you forget this, you’ll end up with many issues involving undefined
elements.
10.5.4 Useful functions
There are filtering functions dedicated to simplifying item selection. Below is a list containing those mostly used in Shiny.
10.5.4.1 Travel in the DOM
Method | Description |
---|---|
children() | Get the children of each element passed in the selector (important: only travels a single level down the DOM tree) |
first() | Given a list of elements, select the first item |
last() | Given a list of elements, select the last item |
find() | Look for a descendant of the selected element(s) that could be multiple levels down in the DOM |
closest() | Returns the first ancestor (including itself) matching the condition (travels up in the DOM) |
filter() | Fine-tune element selection by applying a filter. Only return elements for which the condition is true |
siblings() | Get all siblings of the selected element(s) |
next() | Get the immediately following sibling |
prev() | Get the immediately preceding sibling |
not() | Given an existing set of selected elements, exclude element(s) that match the given condition |
10.5.5 Chaining jQuery methods
A lot of jQuery methods may be chained from one method to the next using a .
.
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
We end the chain by ;
and each step is indented by two spaces in the right direction:
$('ul')
.first()
.css('color', 'green') // add some style with css
.attr('id', 'myAwesomeItem') // add an id attribute
.addClass('amazing-ul');
10.5.6 Iterations
Like in JavaScript, it is possible to do iterations with jQuery. Let’s consider the following HTML elements:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
Many jQuery methods have an implicit iteration. For instance, to change the style of each matched element, we don’t need to do as below:
$('li').each(function() {
$(this).css('visibility', 'hidden'); // Hides all li items
; })
Instead, we just have to write:
$('li').css('visibility', 'hidden');
which is much better. The map
method has a different purpose. It creates a new object based on the provided one:
const items = [0, 1, 2, 3, 4, 5];
const threshold = 3;
let filteredItems = $.map(items, function(i) {
// removes all items > threshold
if (i > threshold)
return null;
return i;
; })
10.5.7 Events
In jQuery there are a significant number of methods related to events. Below are the most popular:
$(element).click(); // click event
$(element).change(); // trigger change on an element
// attach an event handler function.
// Here we add click
$(element).on('click', function() {
// handle event
;
})
// one triggers only once
$(element).one('click', function() {
// handle event
;
})
// useful to trigger plot resize in Shiny so that
// they correctly fit their container
$(element).resize();
// similar to $(element).change();
$(element).trigger('change');
The .on
event is frequently used in Shiny since it allows you to pass custom events that are not part of the JS predefined events. For instance, shinydashboard (Chang and Borges Ribeiro 2018) relies on a specific HTML/JavaScript/CSS template including a homemade API for handling the dashboard events.
10.5.8 Extending objects
A last feature we need to mention about jQuery is the ability to extend objects with additional properties and/or methods.
// jQuery way
$(function() {
let object1 = { apple: 0 };
.extend(object1, {
$print: function() {
console.log(this);
};
}).print();
object1; })
With pure JS we would use Object.defineProperty
:
// pure JavaScript
Object.defineProperty(object1, 'print', {
value: function() {
console.log(this);
,
}writable: false
; })
10.6 Shiny, JavaScript and the HTML inspector
In the part above, we provided some elementary JS knowledge. This section comes back to the main point of this book, which is Shiny. We describe how to leverage the developer tools so as to test, run and debug JavaScript code related to a Shiny app.
10.6.1 The console panel
While developing JS code, we often put some console.log(var)
calls to track the content of a given variable and check that our code is doing what it is supposed to do. The resulting messages, errors or warnings are printing in the console, also called a Read-eval-print loop (REPL), suitable to experiment and practice your new JS/jQuery skills.
10.6.1.1 A real REPL
As a warm-up, run the Shiny app below and open the Chrome DevTools. Notice the two Console
tabs (next to Elements
and at the bottom; the latter may not be visible all the time and can be activated in the parameters), as depicted in Figure 10.5. We recommend using the bottom one to still see the Elements
tab and preview DOM modifications in real time.
Interestingly, you may access any element contained in the window. Copy and paste $("body").addClass("plop");
in the prompt. Notice what happens in the Elements
tab.
10.6.1.2 Track errors and warnings
A lot of Shiny app issues on Stack Overflow or in the RStudio community could be more easily solved by quickly inspecting the console.
10.6.2 Debug Shiny/JS code with the inspector
To debug Shiny apps from the inspector, all your scripts have to be in a folder accessible by the app like the www/
folder or by using addResourcePath()
. Moreover,
if you have minified files, there should be source maps, which will allow reconstructing the original scripts, that is as they were before the minification process. For instance, Shiny has the shiny.min.js.map
. In practice, most R packages bundling HTML templates do not ship these files since they could be quite large (> 1.5MB), and CRAN restricts the package size to 5MB. For instance, the Framework7 HTML template, which shinyMobile is built on (Granjon, Perrier, and Rudolf 2021), has source maps but the size would exceed 5MB, which is obviously too big to include in the R package.
In the following, we consider a very simple Shiny app deployed on shinyapps.io, where a notification is displayed with JavaScript as soon as a user clicks the action button. We deliberately made some typos, the goal being to find and fix them.
- Browse to the app.
- Open the Chrome DevTools.
- Click on the action button (I am pretty sure you clicked before step 2 ;)).
- As expected and shown in Figure 10.6, the console displays an
error message:
Uncaught TypeError: Cannot read property 'show' of undefined
. Sounds good, doesn’t it?
- Expand the error message to show the stack trace. We see that the error occurred during an
onclick
event calling thesendNotif
function. Interestingly, we can open this file by clicking on the provided link (notif.js:2). You should get a layout similar to Figure 10.7, depending on your screen width.
Let’s briefly describe Figure 10.7. On the left side, you can navigate through all files accessible by the web server, that is Shiny internal resources, Shiny external dependencies (like Bootstrap 3), as well as your own scripts. If the app is deployed on shinyapps.io, all scripts are located in a folder starting by
_w_
, which corresponds to the shinyapps.io workerId (This is a detail and not important to understand. See more here.) The central part contains any opened script like a classic IDE. The right side displays debugging tools; you can trigger them by clicking on the corresponding accordion. The scope shows all variables/object values at a break point. Watch allows you to track specific elements, and Event Listener Breakpoints allow you to stop at a given listener type. We could create a new “watcher” by enteringtypeof message
and clicking the add icon to check the message type within thesendNotif
function. Watched expressions are saved when you close the browser.Put a break point at line two by clicking on the left side of the center panel and click again on the action button to trigger the break point. I also additionally set two Watch Expressions (for message and duration) whose type are string and number, respectively, as depicted in Figure 10.8. According to the results, nothing seems wrong for the function arguments.
- The error message
Uncaught TypeError: Cannot read property 'show' of undefined
actually means thatnotification
does not exist. Try yourself by typingShiny.notification
in the console. You’ll getundefined
. Instead, the console suggestsShiny.notifications
. Let’s replace the wrong code in thenotif.js
script and then save it. Click on the “Resume script execution” blue button (top left of the right panel). Notice that a notification is displayed and no more error is thrown.
Congrats! You’ve just debugged your first Shiny app from the web inspector. In practice, your code is probably much more complex than this example, but the workflow remains the same.
10.6.3 The Shiny JavaScript object
The Shiny
object is exported at the top of the shiny.js
file. In other words, we may use this object and any of its properties within the HTML inspector console tab, in any JavaScript file or Shiny app as follows:
ui <- fluidPage(
tags$script(
"$(function() {
console.log(Shiny);
});
"
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)
This object contains many properties and methods as shown in Figure 10.9. Some of particular interest, such as like Shiny.setInputValue
, Shiny.addCustomMessageHandler
, Shiny.shinyapps
, Shiny.bindAll
, will be detailed later in Chapters 12 and 15.
At this point, users may find options(shiny.minified = FALSE)
convenient to debug the Shiny.js core.
10.7 Exercises
Because the JavaScript console is a REPL, all JavaScript exercises may be done inside, except exercise 3, which also involves HTML. In that case, the reader may browse to jsfiddle.
10.7.1 Exercise 1: define variables
- Play with the example below
let myNumber = 1; // initialize
--; // decrement
myNumberconsole.log(myNumber); // print 0
10.7.2 Exercise 2: define objects
Below is an object skeleton.
const me = {
name : ,
age : ,
music : ,
printName: function() {
console.log(`I am ${}`);
} }
- Fill in the objection with some random values.
- Access the
name
property. - Create the
printAge
method, which returns theage
. Hint:this
refers to the object itself. For instancethis.name
gives the name property.
10.7.3 Exercise 3: jQuery
JSFiddle allows you to insert HTML, CSS and JavaScript to test code, share and more. It also does not require you to have any specific configuration on your machine so that you focus on testing!
- Go to JSFiddle.
- Insert the following HTML code chunk in the HTML sub-window.
<!DOCTYPE HTML>
<html>
<head>
<!-- head content here -->
</head>
<body>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
</body>
</html>
This is a very basic HTML skeleton.
- In the JavaScript windows, select jQuery 3.5.1 in the dropdown menu. (Why 3.5.1? The latest Shiny release (v1.6.0) relies on that version. It is therefore best practice to ensure dependencies are similar, at least the major version.)
- Since it is best practice to run jQuery code only when the document is ready (avoiding targeting non existing elements), we wrap our JS code in the following:
$(function() {
// your code
;
})
// or a more explicit syntax
$(document).ready(function() {
// code
; })
- Create an event listener to change the third item color as soon as you click on it.
Hint 1: To select a specific item you may use
$(selector:eq(i))
, wherei
is the index of the element. Keep in mind that JavaScript starts from 0 and not 1 like R! Hint 2: as a reminder, to create an event listener in jQuery, we use the following pattern.
$('selector').on('event_name', function(e) {
// your logic
; })