---
name: "up.watch"
area: "API"
kind: "JavaScript function"
parent: "up.form module"
url: "https://unpoly.com/api/up.watch"
---
<nav aria-label="Unpoly docs">[All docs](https://unpoly.com/index.md) · [up.form module](https://unpoly.com/api/up.form.md)</nav>

# up.watch(element, [options], callback)

Watches form fields and runs a callback when a value changes.

While you can also listen to a [standard `input` event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event), `up.watch()` adds the following behavior:

- The callback only runs when a value was actually changed. Multiple events resulting in the same value will only run the callback once.
- The callback's execution frequency can be [debounced](https://unpoly.com/learn/watch-options.md#debouncing).
- Guarantees that [only one async callback is running concurrently](#async-callbacks).

<nav aria-label="Contents">

#### Contents

- [Example](#example)
- [Callback arguments](#callback-arguments)
- [Watching multiple fields](#watching-multiple-fields)
- [Async callbacks](#async-callbacks)
- [Batching changes](#batching-changes)
- [Parameters](#parameters)
- [Return value](#return-value)

</nav>

## Example {#example}

The following would print to the console whenever an input field changes:

```js
up.watch('input.query', function(value) {
  console.log('Query is now', value)
})
```

## Callback arguments {#callback-arguments}

The watch callback may accept up to three arguments that describe the observed change:

```js
up.watch('input.query', function(value, name, options) {
  console.log('Query is now', value)
})
```

Here is a full description of the individual callback arguments:

| Name | Type | Description |
| --- | --- | --- |
| `value` | `string` | The changed field value. |
| `name` | `string` | The `[name]` of the changed field. |
| `options.origin` | [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element) | The element that caused the change. This is usually the changed field. |
| `options.feedback` | `boolean` | Whether to set [feedback classes](https://unpoly.com/learn/feedback-classes.md) while working. Parsed from the field's `[up-watch-feedback]` attribute. |
| `options.disable` | `boolean` | Which [fields to disable](https://unpoly.com/learn/disabling-forms.md) while working. Parsed from the field's `[up-watch-disable]` attribute. |
| `options.preview` | `string` | The name of a [preview](https://unpoly.com/learn/previews.md) to run while working. Parsed from the field's `[up-watch-preview]` attribute. |
| `options.placeholder` | `string` | The HTML or selector for a [placeholder](https://unpoly.com/learn/placeholders.md) to show while working. Parsed from the field's `[up-watch-placeholder]` attribute. |

When rendering from a watch callback, you should forward the `options` to the rendering function:

```js
up.watch('input.query', function(value, name, options) {
  return up.reload('main', { ...options, params: { query: value }})
})
```

> #### Tip
>
> A rendering watch callback can often be replaced with [`[up-autosubmit]`](https://unpoly.com/api/up-autosubmit.md).

## Watching multiple fields {#watching-multiple-fields}

Instead of a single form field, you can also pass multiple fields, a `<form>` or any container that contains form fields. The callback will be run if any of the given fields change:

```js
up.watch('form', function(value, name) {
  console.log('The value of %o is now %o', name, value)
})
```

## Async callbacks {#async-callbacks}

When your callback does async work (like fetching data over the network), it should return a promise that settles once the work concludes:

```js
up.watch('input.query', function(value, name, options) {
  let url = '/search?query=' + encodeURIComponent(value)
  return up.render('.results', { url, ...options })
})
```

Unpoly will guarantee that only one async callback is running concurrently. If the form is changed while an async callback is still processing, Unpoly will wait until the callback concludes and then run it again with the latest field values.

You can also return a promise by using `async` / `await`:

```js
up.watch('input.query', async function(value, name, options) {
  let url = '/search?query=' + encodeURIComponent(value)
  await up.render('.results', { url, ...options })
})
```

## Batching changes {#batching-changes}

You may also pass the `{ batch: true }` option to receive all changes since the last callback in a single object:

```js
up.watch('form', { batch: true }, function(diff, options) {
  for (let name in diff) {
    let value = diff[name]
    console.log('The value of %o is now %o', name, value)
  }
})
```

---

## Parameters {#parameters}

### Observed events {#parameters-observed-events}

#### element {#element}

(required)

The form field that will be watched.

You can pass a field, a `<form>` or any container that contains form fields. The callback will be run if any of the contained fields change.

Type: [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) | [jQuery](https://learn.jquery.com/using-jquery-core/jquery-object/)

#### [options.event='input'] {#options.event}

(optional)

The types of event to observe.

See [which events to watch](https://unpoly.com/learn/watch-options.md#events).

Type: string | [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<string>

#### [options.delay=0] {#options.delay}

(optional)

The number of milliseconds to wait between an observed event and running the callback.

See [debouncing callbacks](https://unpoly.com/learn/watch-options.md#debouncing).

Type: number

### Callback {#parameters-callback}

#### [options.batch=false] (Experimental) {#options.batch}

(optional)

If set to `true`, the callback will receive multiple detected changes in a [single diff object as its argument](#batching-changes).

The object's keys are the names of the changed fields. The object's values are the values of the changed fields.

Type: boolean

#### callback {#callback}

(required)

The callback to run when the field's value changes.

The callback is called with [arguments](#callback-arguments) that describe the change.

An [async callback function must return a promise](#async-callbacks) that settles when the callback completes.

Type: Function(value, name, options): [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises)

---

## Return value {#return-value}

A destructor function that unsubscribes the watcher when called.

Watching will stop automatically when the form is [destroyed](https://unpoly.com/api/up.destroy.md).

Type: Function()

