+ - 0:00:00
Notes for current slide
Notes for next slide

OK Computer

Interacting with the Browser

Garrick Aden-Buie

rstudio::conf(2020, "JavaScript for Shiny Users")

1

Arrays
& Objects

2

👨🏼‍💻
[live coding]

3

Meet Object

  • Objects are like R's lists (except used a whole lot more in JS)
  • Each entry has a name called a key and a value.
  • The value is a property if it's a constant or a method if it's a function
  • Access elements with .dot or [bracket] notation
  • Undefined properties are ... undefined
  • const means can't reasign the whole object
const stats = {pkg: 'dplyr', downloads: 893}
stats.pkg
stats['downloads']
const propName = 'downloads'
stats[propName]
stats.rank
stats.downloads = 893
stats
{"pkg":"dplyr","downloads":893} 
4

Creating an object from variables

  • Turn your variables into objects with this one easy trick
let pkg = 'dplyr'
let downloads = 893
const stats = {pkg: 'dplyr', downloads: 893}
stats = {pkg, downloads}
[TypeError] Assignment to constant variable. 
5

Objects can hold arrays or more objects

  • Objects, like lists, hold anything: strings, numbers, arrays, other objects
  • Get the array of object keys with Object.keys()
let stats = {
pkg: 'dplyr',
downloads: 893,
depends: ['glue', 'magrittr', 'rlang']
}
let future = {}
stats.version = {major: 0, minor: 8, patch: 3}
Object.keys(stats)
Object.keys(stats.depends)
Object.keys(future)
[] 
6

Meet Array

  • Arrays are also collection, but have indices instead of keys
  • Arrays look a lot like R's vectors but...
  • ...arrays can hold anything
  • What makes them special: indices, order, length
let pkgs = ['dplyr', 'ggplot2', 'tidyr', 'shiny']
let downloads = [893, 762, 679, 395]
let random = [1, 2, 'three']
let nothingYet = []
pkgs.length
4 
7

Array Indexing

  • Arrays are homogeneous and can even include additional arrays
  • JavaScript is zero indexed
  • Bracket notation gets you one entry only
  • Arrays != vectors
let pkgs = [['dplyr', 'tidyr'], 'ggplot2', 'shiny']
pkgs[0]
pkgs[0][1]
pkgs * 1000
NaN 
8

Arrays come with properties & methods

//property
pkgs.length
// some methods don't change the array
pkgs.includes('shiny')
pkgs.slice(1, 4)
// some methods do!
pkgs.push('glue')
pkgs.pop()
// some do not clearly state their intentions
pkgs.sort()
["dplyr","ggplot2","shiny","tidyr"] 
9

Arrays are objects

  • Arrays are actually objets with special properties
  • One of those properties is that the keys are indices
  • What typeof thing is this? But is it an instanceof?
Object.keys(pkgs)
let stats = {pkg: pkgs[0], downloads: 900}
typeof pkgs
typeof stats
stats instanceof Object
pkgs instanceof Object
pkgs instanceof Array
true 
10

Yeah, but is it an array?

  • More helpfully Array has a method
Array.isArray(pkgs)
true 
11

Prototype & Inheritance

12

Prototype & Inheritance

13

Prototype & Inheritance

14

Prototype & Inheritance

15

Prototype & Inheritance

16

Prototype & Inheritance

17

Prototype & Inheritance

18

Prototype & Inheritance

19

Array Practice

A common pattern you'll see often is to initialize an empty array [] that is built up over time.

  1. Give yourself an empty pkgs = []

  2. 30 seconds to think of your favorite packages using .push() to add each package.

  3. Follow up:

    • How many packages did you think of?
    • What's the 3rd package your thought of?
    • What's the last package you thought of?
    • Get the last package but also remove it from your list.
    • Alphabetize your packages.
00:30
20

Functions

21
There are too many ways to write functions
22

The Forms of Function

function increment(x, by) {
return x + by
}
  • Most popular
  • Use it before you define it (hoisted)
23

The Forms of Function

function increment(x, by) {
return x + by
}
  • Most popular
  • Use it before you define it (hoisted)
const increment = function(x, by) {
return x + by
}
  • What is this, R?
  • Define it first
24

The Forms of Function

function increment(x, by) {
return x + by
}
  • Most popular
  • Use it before you define it (hoisted)
const increment = function(x, by) {
return x + by
}
  • What is this, R?
  • Define it first
function(x, by) {
return x + by
}
  • 🕵🏼 Anonymous
  • Used in callbacks
  • or must be called right away (function() {...})()
25
👩🏽‍💻

js4shiny::repl_js()

26
function increment(x, by) {
return x + by
}
increment(1, 2)
27
  • with and without default arguments
  • can you reference the argument in the function name?
  • arguments are positional
  • arguments without defaults and without values?
  • const name = function()
const increment = (x, by) => {
return x + by
}
increment(30, 12)
28

Functional Recommendations

General Functions

  • Use these just about anywhere
  • Don't think too hard when you read it later
function increment(x, by) {
return x + by
}

Anonymous Functions

  • Use when it fits on one line
  • Or anywhere you'd use
    ~ { .x + by }
const inc = (x, by) => x + by
29

Refactor the JavaScript in our first page

  1. Create cities: an array of objects with the city name and age for

    • San Francisco — 38.8
    • Los Angeles — 35.9
  2. Write a function updateCityAge that takes a city and age and updates the document

  3. In the browser, use the array and function to switch between San Francisco and Los Angeles.

03:00

repl_example("first-page-04")

30
const cities = [
{city: 'San Francisco', age: 38.8},
{city: 'Los Angeles', age: 35.9}
]
function updateCityAge(city, age) {
console.log(
`${city} residents are ~${age} years old`
)
}
updateCityAge(cities[0].city, cities[0].age)
updateCityAge(cities[1].city, cities[1].age)
31

Browser Events

32

Reactions, not reactivity

33

Reactions, not reactivity

34

Reactions, not reactivity

35

Reactions, not reactivity

36

Reactions, not reactivity

37

Reactions, not reactivity

38

Reactions, not reactivity

39

Reactions, not reactivity

40

Reactions, not reactivity

41

Reactions, not reactivity

42

Reactions, not reactivity

43

Adding Event Listeners

You need

  1. An event type to listen for
  2. An an element to spy on
  3. A callback (function) to run when the event happens
44

Adding Event Listeners

You need

  1. An event type to listen for
  2. An an element to spy on
  3. A callback (function) to run when the event happens

element.addEventListener('click', function (event) {
// ... do things ...
// ... possibly info in event
})

45

👨🏼‍💻
[live coding]

repl_example("first-page-05")

46

Add Button

<button id="next-city" value="0">Next City</button>

Use first city on load

updateCityAge(cities[0])
47

Event listen to console.log()

  • What function will we use to find the button?
const btn = document.getElementById('next-city')
btn.addEventListener('click', function(event) {
console.log(event)
})

Find the info we want to get from the event

event.target

btn.addEventListener('click', function(event) {
console.log(event.target.value)
})
48

The button's value === current city

btn.addEventListener('click', function(event) {
let btnValue = event.target.value
console.log(cities[btnValue])
})
49

btnValue + 1 and show that city

Note we could have done btn.value to get the value, too What happens if you just do btnValue + 1?

btn.addEventListener('click', function(event) {
let btnValue = event.target.value
btnValue = Number(btnValue) + 1
btn.value = btnValue
updateCityAge(cities[btnValue])
})
50

If the counter > length of cities, start over

btn.addEventListener('click', function(event) {
let btnValue = event.target.value
btnValue = Number(btnValue) + 1
if (btnValue >= cities.length) {
btnValue = 0
}
btn.value = btnValue
updateCityAge(cities[btnValue])
})
[
{city: 'Fresno', age: 31.8},
{city: 'Santa Rosa', age: 41.4}
]
51

JSON, quickly

const cities = [
{
city: 'San Francisco',
age: 38.8
},
{
city: 'Los Angeles',
age: 35.9
}
]
[
{
"city": "San Francisco",
"age": 38.8
},
{
"city": "Los Angeles",
"age": 35.9
}
]
52

JSON, quickly

const cities = [
{
city: 'San Francisco',
age: 38.8
},
{
city: 'Los Angeles',
age: 35.9
}
]
[
{
"city": "San Francisco",
"age": 38.8
},
{
"city": "Los Angeles",
"age": 35.9
}
]
const citiesAsJson = JSON.stringify(cities); citiesAsJson
[{"city":"San Francisco","age":38.8},{"city":"Los Angeles","age":35.9}] 
53

JSON, quickly

const cities = [
{
city: 'San Francisco',
age: 38.8
},
{
city: 'Los Angeles',
age: 35.9
}
]
[
{
"city": "San Francisco",
"age": 38.8
},
{
"city": "Los Angeles",
"age": 35.9
}
]
JSON.parse(citiesAsJson)[0]
{"city":"San Francisco","age":38.8} 
54

Add More Data!

  • Run repl_example("first-page-07")

  • I added JSON data inside <script id="data-cities">

  • Find the element and save its .textContent to a variable

  • Use JSON.parse() to convert the JSON string to data in place of cities.

  • Watch out! The data calls age median_age, make sure you update your function.

  • Bonus: Search MDN for destructuring assignment and use the Object destructuring section to learn how to assign a property to a new variable name

  • Bonus: Add median_home_price to your page, too.

05:00
55

Styles and JavaScript in External Files

Our index.html is getting crowded.

Styles: style.css

<head>
<link href="style.css" rel="stylesheet" />
</head>

Script: script.js

// last thing in <body>
<script src="script.js"></script>

repl_example("first-page-09")

56

Can discuss placement of either here

If I have extra time I can demo

repl_example("event-types")

Arrays
& Objects

2
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
oTile View: Overview of Slides
Esc Back to slideshow