Published
Edited
Aug 30, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
page = html`<div class="Counter"></div>` // This is what the component would look like in the HTML file.
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
/* Counter component code */
const main = () => {
// This code runs the render function once to initialize the element.
// Usually, this code would be located in a driver script.
// We want to select all of the elements in the document (this page)
// that have class="Counter"
// and render a counter component into each one.
document.querySelectorAll(".Counter").forEach(element => {
// We'll initialize all values to 0.
render({element: element, state: {value: 0}}) // See the definition of render() below.
})
}
const render = ({element, state}) => {
console.debug(`Rendering`, state, `into`, element)
/* If our state looks like this:
{
value: 3
}
*/
/* Then the counter is going to look like this:
<div class="Counter">
<div id="root"> <-- This will contain the actual component--only our code touches this element.
<span id="current_value">3</span> <--- This will show the value.
<button id="increment">Increment</button> <--- When we click this button, the value increases by one.
</div>
</div>
*/
// Read further to see how this happens.
// First, we select the old root element.
// If this is the first time we're rendering, we'll need to create it.
let old_root = element.querySelector("#root")
if (!old_root) {
old_root = document.createElement('div')
element.appendChild(old_root)
}
// Next, we generate a *new* root element from scratch.
// We're going to configure this element to reflect our state.
const new_root = document.createElement('div')
new_root.id = "root"
// This sets up the new root element but does *not* attach any handlers.
const {value} = state // This uses ES6 destructuring to get `value` from state.
new_root.innerHTML = `
<span id="current_value">${value}</span>
<button id="increment">Increment</button>
<button id="reset">Reset</button>`
/* Note this uses ES6 template syntax--if value is 3,
`abc${value}def` becomes `abc3def`. */
// Now that the interface exists, we can attach event handlers.
// When we click the increment button, we want to modify part
// of the state by increasing `value` by 1.
new_root.querySelector('#increment').onclick =
() => update_state({value: value+1}) // See the definition of update_state below.
// When we click the reset button, we want to set `value` to 0.
new_root.querySelector('#reset').onclick =
() => update_state({value: 0})
// This is how we update the state.
const update_state = update_object => {
console.debug(`Updating`, state, `with a partial state`, update_object,
`and rerendering (after yielding execution).`)
// It doesn't matter here, but if we're handling "continuous" input
// (i.e. from a mouse), we have to yield execution with setTimeout so
// the browser can continue capturing events.
setTimeout(render({element: element, state: {...state, ...update_object}}))
}
// Now that the new root element has been created, we replace
// the old root element with the new root element.
old_root.parentElement.replaceChild(new_root, old_root)
}
main() // (Runs the main function.)
page // (Ignore this.) Tells Observable this cell should rerun if the `page` cell changes.
return "Read this!"
}
Insert cell
// (This is the Counter's styling. It would usually be found in a CSS file.)

html`<style>
.Counter {
display:inline-block;
border: 1px solid black;
padding: 5px;
}</style>`
Insert cell
Insert cell
Insert cell
function showable ({text, hidden_text=md`**Click to show**`, show=false}) {
const el = html`${show ? text : hidden_text}`
el.onclick = () => {
show = !show
el.parentNode.replaceChild(showable({text, show, hidden_text}), el)
}
return el
}
Insert cell
import {toc} from "@bryangingechen/toc"
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more