# Custom Input Trouble

**URL:** <https://talk.observablehq.com/t/custom-input-trouble/9244>\
**Category:** Help\
**Created:** [May 9, 2024, 2:33am UTC](https://talk.observablehq.com/t/custom-input-trouble/9244 "2024-05-09T02:33:35Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![charlieprior](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/charlieprior/32/8871_2.png) [@charlieprior](https://talk.observablehq.com/u/charlieprior)\
**Post date:** [May 9, 2024, 2:33am UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/1 "2024-05-09T02:33:35Z")

</div>

I’m trying to make a custom input that is a box that the user can click on that returns the relative position (in percent) within the box. However all I get out when I try to use the variable is

```auto
AsyncGenerator {
  <prototype>: 
  AsyncGenerator {}
}

```

Also, I’m trying to eventually bind the two values to two different sliders (so that the user can either select a value by individually moving the sliders or clicking in the box), so I’m also not even sure if this is the best way to go about that.

Here’s my code:

```auto
    import {html} from "htl";

    function clickBox() {
        let value;
        const style = {
            width: "100px",
            height: "100px",
            cursor: "crosshair",
            border: "1px solid black"
        };

        const box = html`<div style=${style} onclick=${event => {
            const rect = event.currentTarget.getBoundingClientRect();
            const x = (event.clientX - rect.left) / (rect.right - rect.left);
            const y = (event.clientY - rect.top) / (rect.bottom - rect.top);
            value = [x, y];
        }}>`;

        return Object.defineProperty(box, "value", {
            get: () => value,
            set: newValue => value = newValue
        });
    }

    const cb = view(clickBox());

    display(cb);

```

If I move display to its own cell, nothing shows up.

Thank you all so much!

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [May 9, 2024, 11:25am UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/2 "2024-05-09T11:25:59Z")

</div>

Your `onclick` callback needs to dispatch an “input” event to let the `view()` generator know that it needs to produce a new value:

```js
box.dispatchEvent(new Event("input", {bubbles: true}));

```

Depending on your requirements you may also want to assign an initial `value`, otherwise downstream (i.e. dependent) blocks won’t resolve until after the first click.

* * *

`view()` returns an async generator that should not be consumed directly in the same block.

Normally a generator can only have one consumer because it yields each value only once, but when `cb` is referenced from other blocks, Framework’s Runtime will take care of resolving the generator’s promises and storing the values so that they can be passed to multiple blocks.

---

<div class="post-metadata">

**Author:** ![charlieprior](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/charlieprior/32/8871_2.png) [@charlieprior](https://talk.observablehq.com/u/charlieprior)\
**Post date:** [May 9, 2024, 2:33pm UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/3 "2024-05-09T14:33:16Z")

</div>

Thank you, that fixed it.

Do you know how I can link the value to my two existing sliders which look like:

```auto
const sin2 = view(Inputs.range([0, 1], {label: tex`\sin^2(\theta_{14})`, step: 0.01}));
const deltam = view(Inputs.range([0, 5], {label: tex`\Delta m_{14}^2 [eV^2]`, step: 0.05}));

```

I’ve tried assigning the views to Mutables, and using Inputs.bind(), but I can’t get either to work. Basically I can’t figure out a good way to extract the array to two values.

If there’s some way to set a Mutable from an input all my problems would be solved.

---

<div class="post-metadata">

**Author:** ![charlieprior](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/charlieprior/32/8871_2.png) [@charlieprior](https://talk.observablehq.com/u/charlieprior)\
**Post date:** [May 9, 2024, 3:19pm UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/4 "2024-05-09T15:19:59Z")

</div>

Ok I figured it out.

For reference, this is how I ended up doing it.

```js
    let sin2 = Mutable(0);
    const setSin2 = (value) => {
        sin2.value = value;
    }

    let deltam = Mutable(0);
    const setDeltam = (value) => {
        deltam.value = value;
    }

```

```js
    const L = view(Inputs.range([0, 50], {label: html`Distance <i>[m]</i>`, step: 0.1}));
    const sin2slider = view(Inputs.range([0, 1], {label: tex`\sin^2(\theta_{14})`, step: 0.01}));
    const deltamslider = view(Inputs.range([0, 5], {label: tex`\Delta m_{14}^2 [eV^2]`, step: 0.05}));

```

```js
    setSin2(sin2slider);
    setDeltam(deltamslider);

```

```js
document.getElementById('clickme').onclick = function(e) {
      // e = Mouse click event.
      var rect = e.currentTarget.getBoundingClientRect();
      var x = (e.clientX - rect.left)/(rect.right - rect.left); //x position within the element.
      var y = (e.clientY - rect.top)/(rect.bottom - rect.top); //y position within the element.
      setSin2(x);
      setDeltam(5*y);
}

```

Please let me know if there’s a better way!!

---

<div class="post-metadata">

**Author:** ![charlieprior](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/charlieprior/32/8871_2.png) [@charlieprior](https://talk.observablehq.com/u/charlieprior)\
**Post date:** [May 9, 2024, 3:32pm UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/5 "2024-05-09T15:32:38Z")

</div>

The only problem is it doesn’t update the value next to the slider 🙃

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [May 10, 2024, 3:53am UTC](https://talk.observablehq.com/t/custom-input-trouble/9244/6 "2024-05-10T03:53:53Z")

</div>

Here’s my take on it:

[https://observablehq.observablehq.cloud/framework-example-custom-input-2d/](https://observablehq.observablehq.cloud/framework-example-custom-input-2d/)

The custom input is essentially the same as described above. I declared the custom input and its value like so:

```js
const xyInput = Range2D();
const xy = view(xyInput);

```

Then I separately declared the 1D range inputs:

```js
const xInput = Inputs.range([0, 1], {label: "x", step: 0.01});
const yInput = Inputs.range([0, 1], {label: "y", step: 0.01});
const x = view(xInput);
const y = view(yInput);

```

Finally I used event listeners for bidirectional binding, checking if the event bubbles to distinguish direct and indirect input:

```js
xyInput.oninput = (event) => {
  if (!event.bubbles) return;
  xInput.value = xyInput.value[0];
  yInput.value = xyInput.value[1];
  xInput.dispatchEvent(new Event("input"));
  yInput.dispatchEvent(new Event("input"));
};
xInput.oninput = yInput.oninput = (event) => {
  if (!event.bubbles) return;
  xyInput.value = [xInput.value, yInput.value];
  xyInput.dispatchEvent(new Event("input"));
};

```

There’s probably another way to do it, but, hey, it works. 😅
