Button input component in Observable Framework — trigger actions on click.
Observable Framework documentation: Input: Button Source: https://observablehq.com/framework/input-button
<a href="https://github.com/observablehq/inputs/blob/main/README.md#button">API</a> · <a href="https://github.com/observablehq/inputs/blob/main/src/button.js">Source</a> · The button input emits an input event when you click it. Buttons may be used to trigger the evaluation of cells, say to restart an animation. For example, below is an animation that progressively hides a bar. Clicking the button will restart the animation.
<canvas id="canvas" width="360" height="20" style="max-width: 100%; color: var(--theme-foreground-focus); border: solid 1px var(--theme-foreground);"></canvas>
const replay = view(Inputs.button("Replay"));
<div class="note">The <code>progress</code> top-level variable is declared as a <a href="../reactivity#generators">generator</a>. This causes the Observable runtime to automatically advance the generator on each animation frame. If you prefer, you could write this animation using a standard <code>requestAnimationFrame</code> loop, but generators are nice because the animation will automatically be interrupted when the code is <a href="../reactivity#invalidation">invalidated</a>.</div>
The code block below references <code>replay</code>, so it will run automatically whenever the replay button is clicked.
const canvas = document.querySelector("#canvas");
const context = canvas.getContext("2d");
context.fillStyle = getComputedStyle(canvas).color;
replay; // run this block when the button is clicked
const progress = (function* () {
for (let i = canvas.width; i >= 0; --i) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillRect(0, 0, i, canvas.height);
yield canvas;
}
})();
You can also use buttons to count clicks. While the value of a button is often not needed, it defaults to zero and is incremented each time the button is clicked.
const clicks = view(Inputs.button("Click me"));
clicks
You have clicked ${clicks} times.
You have clicked ${clicks} times.
While buttons count clicks by default, you can change the behavior by specifying the value and reduce options: value is the initial value, and reduce is called whenever the button is clicked, being passed the current value and returning the new value. The value of the button below is the last time the button was clicked, or null if the button has not been clicked.
const time = view(Inputs.button("Update", {value: null, reduce: () => new Date}));
time
Note that even if the value of the button doesn’t change, it will still trigger any cells that reference the button’s value to run. (The Observable runtime listens for input events on the view, and doesn’t check whether the value of the view has changed.)
For multiple buttons, pass an array of [content, reduce] tuples. For example, to have a counter that can be incremented, decremented, and reset:
const counter = view(Inputs.button([
["Increment", value => value + 1],
["Decrement", value => value - 1],
["Reset", value => 0]
], {value: 0, label: "Counter"}));
counter
The first argument to Inputs.button() is the contents of the button. It’s not required, but is strongly encouraged.
const x = view(Inputs.button());
The contents of the button input can be an HTML element if desired, say for control over typography.
const y = view(Inputs.button(html`<i>Fancy</i>`));
Like other basic inputs, buttons can have an optional label, which can also be either a text string or an HTML element.
const confirm = view(Inputs.button("OK", {label: "Continue?"}));
You can change the rendered text in Markdown based on whether a button is clicked. Try clicking the OK button with the Continue? label.
confirm ? "Confirmed!" : "Awaiting confirmation…"
${confirm ? "Confirmed!" : "Awaiting confirmation…"}
You can also use a button to copy something to the clipboard.
Inputs.button("Copy to clipboard", {value: null, reduce: () => navigator.clipboard.writeText(time)})
Inputs.button(content, options)
The available button input options are: