{ojs}
```.button("Confirm", {label: "Click me!"})
Inputs ```
1 Overview
The documents regarding how to use Observable Javascript in Quarto is extremely lacking. Two main sources for this topic are quarto.org and quarto dashboard.
Because ojs cells cannot be executed by simply clicking on “Run” button in Rstudio, instead, you can only see the result in the rendering step. Thus, it is always recommended to use observable notebook to build ojs and copy-paste into Quarto document.
Interesting, the starting place for observablejs is their github page - observablehq/stdlib, which include standard libraries for observablejs.
For data visualization lovers, Inputs
(Quarto doc; OJS GitHub doc) and Plot
(Quarto doc) are the two most important libraries in OJS.
2 Inputs Library
2.2 Checkbox
{ojs}
```.checkbox(["Torgersen", "Biscoe", "Dream"],
Inputs{value: ["Torgersen", "Biscoe"], label: "Islands:"})
```
3 OJS 101
This section is based on the hands-on tutorial in official observable js website - Learn Just Enough JavaScript: Introduction.
3.1 Variables
{ojs}
```= "Javascript is cool!"
myVariable
myVariable ```
{ojs}
```= 25
myNumber
myNumber ```
Type | Description | Example |
---|---|---|
Number | A numeric value |
|
Boolean | A true or false value |
|
String | A set of characters in single or double quotes |
|
Null | A value represent the intentional absence of a value |
|
Array | A collection of elements |
|
Object | An element with key-value pairs |
|
Date | A special object for representing dates |
|
3.2 Objects
In OJS, objects are similar to named vectors in R, following the format ({key1: value1; key2:value2})
. Do not forget the parenthesis to make OJS object work in Quarto.
{ojs}
```= ({name:"Paul", age:25})
myObject .name
myObject ```
3.3 Arrays
Arrays in OJS is similar to list (data.frame) in R and tuple in Python. It is defined with braces []
. Note that same to python, the index of first element in OJS is 0.
{ojs}
```= [1, 2, 3]
myArray1 [2] // the third element
myArray1 ```
{ojs}
```= [[1, 2], [3, 4]]
myArray2 [1][1]
myArray2 ```
{ojs}
```= [1, 'cat', ({name: "ketty"})]
myArray3 [2]
myArray3 ```
{ojs}
```[2].name
myArray3 ```
3.4 Functions
{ojs}
```add(x, y) {
function return x + y
}
add(1, 3)
```
3.5 Conditions
Using a double equal sign == is a logical test to see if two values are the same. In JavaScript, we have different types of values, like numerical values or strings. If you use a triple equal sign ===, you not only check if the values are the same, but you check if the value types are the same.
{ojs}
```1 == 1 // true
1 == '1' // true
1 === '1' // false
```
3.6 Loops
For loop follows the format for (let i = ${starting}; ${end condition}, i ++)
. Note that let
is neccessary in let string=''
to define a constant variable.
{ojs}
```{
=''
let stringfor (let i = 0; i <=5; i ++){
+= i
string }
return string
}
```
Given a array, we can loop through the array.
{ojs}
```= [1, 20, 13, 4, 55, 6]
myValues ```
{ojs}
```{
= 0; // Declare a variable for the largest number
let largestNumber for(let i = 0; i < myValues.length - 1; i++) { // Loop through all the values in my array
if(myValues[i] > largestNumber) { // Check if the value in the array is larger that the largestNumber
= myValues[i] // If so, assign the value as the new largest number
largestNumber }
}
return largestNumber
}
```
You can also use the helper function in D3.js.
{ojs}
```.max(myValues)
d3 ```
One example of repeating animation using while
loops.
{ojs}
```{
const width = 300;
const height = 100;
const r = 30;
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height);
const circle = svg.append('circle')
.attr('r', r)
.attr('cy', height / 2)
.attr('cx', r);
= 30;
let cx while(true) { // Loop goes on forever
yield svg.node();
.delay(2000); // This causes the loop to "wait" 2000 milliseconds
await Promises== r ? cx = width - r : cx = r;
cx .transition()
circle.duration(1500)
.attr('cx', cx);
}
}
```
4 Example 1: Palmer Penguins
Currently, OJS doesn’t work interactively with RStudio (see the github discussion). As the document shows, the example based on Allison Horst’s Palmer Penguins.
```{r}
#| message: false
#| output: false
library(palmerpenguins)
library(here)
data(penguins)
write.csv(penguins, file = here("notes", "2024",'2024-03-10-Quarto-Observable-JS', 'palmer-penguins.csv'))
```
{ojs}
```= FileAttachment('palmer-penguins.csv').csv({ typed: true})
data ```
{ojs}
```= Inputs.range(
viewof bill_length_min [32, 50],
{value: 35, step: 1, label: "Bill length (min):"}
)
= Inputs.checkbox(
viewof islands ["Torgersen", "Biscoe", "Dream"],
{ value: ["Torgersen", "Biscoe"],
: "Islands:"
label}
)
```
{ojs}
```= data.filter(function(penguin) {
filtered return bill_length_min < penguin.bill_length_mm &&
.includes(penguin.island);
islands})
```
{ojs}
```.rectY(filtered,
Plot.binX(
Plot{y: "count"},
{x: "body_mass_g", fill: "species", thresholds: 20}
))
.plot({
: {
facet: filtered,
data: "sex",
x: "species",
y: 80
marginRight},
: [
marks.frame(),
Plot]
}
)
```
5 Transfer from R to OJS
There’s a nice post from Duke library, introducing a nicer way of embedding R and OJS. The getting-started page of OJS official website has many examples of different types of plots.
```{r}
#| output: false
ojs_define(penguins = penguins)
```
5.1 Dot (scatter) plot
{ojs}
```.plot({
Plot: true,
grid: 10,
inset: [
marks.dot(transpose(penguins), {
Plot: "bill_length_mm",
x: "bill_depth_mm",
y: "species"
stroke}),
]
})
```
5.2 Histogram
```{r}
#| output: false
library(tidyverse)
<- penguins |>
penguins_summarized mutate(body_mass_group = cut(body_mass_g / 1000, 10)) |>
group_by(body_mass_group) |>
summarise(frequency = n())
ojs_define(penguins_summarized = penguins_summarized)
```
{ojs}
```.plot({
Plot: {padding: 0.1},
x: 50,
marginTop: 0,
marginRight: 50,
marginBottom: [
marks.barY(transpose(penguins_summarized), {x: "body_mass_group", y: "frequency", dx: 2, dy: 2}),
Plot.barY(transpose(penguins_summarized), {x: "body_mass_group", y: "frequency", fill: "green", dx: -2, dy: -2})
Plot]
})
```