diff --git a/README.md b/README.md
index 4a42a24..4fb70c6 100644
--- a/README.md
+++ b/README.md
@@ -23,41 +23,54 @@ hard-to-follow callback chains.
```js
// Filtering and mapping:
-element
- .on('click')
- .filter((e) => e.target.matches('.foo'))
- .map((e) => ({ x: e.clientX, y: e.clientY }))
- .subscribe({ next: handleClickAtPoint });
+element.on('click')
+ .filter(e => e.target.matches('.foo'))
+ .map(e => ({x: e.clientX, y: e.clientY }))
+ .subscribe(({ x, y }) => {
+ console.log(`Clicked foo at ${x}, ${y}`)
+ }));
+```
+
+
+Imperative version
+
+```js
+element.addEventListener('click', (e) => {
+ if (e.target.matches('.foo')) {
+ const { clientX: x, clientY: y } = e;
+ console.log(`Clicked foo at ${x}, ${y}`);
+ }
+});
```
+
+
#### Example 2
+Automatic, declarative unsubscription via the takeUntil method
+
```js
-// Automatic, declarative unsubscription via the takeUntil method:
-element.on('mousemove')
- .takeUntil(document.on('mouseup'))
- .subscribe({next: e => … });
-
-// Since reduce and some other terminators return promises, they also play
-// well with async functions:
-await element.on('mousemove')
- .takeUntil(element.on('mouseup'))
- .reduce((soFar, e) => …);
+const ac = new AbortController();
+element
+ .on('mousemove')
+ .takeUntil(element.on('mouseup'))
+ .subscribe(console.log, { signal: ac.signal });
```
Imperative version
```js
-// Imperative
-const controller = new AbortController();
+const ac = new AbortController();
element.addEventListener(
'mousemove',
(e) => {
- element.addEventListener('mouseup', (e) => controller.abort());
+ element.addEventListener('mouseup', (e) => ac.abort(), {
+ signal: controller.signal,
+ });
console.log(e);
},
- { signal: controller.signal },
+ { signal: ac.signal },
);
```
@@ -65,20 +78,33 @@ element.addEventListener(
#### Example 3
-Tracking all link clicks within a container
([example](https://github.com/whatwg/dom/issues/544#issuecomment-351705380)):
```js
+let linkClicks = 0;
container
.on('click')
.filter((e) => e.target.closest('a'))
- .subscribe({
- next: (e) => {
- // …
- },
+ .subscribe(() => {
+ linkClicks++;
});
```
+
+Imperative version
+
+```js
+let linkClicks = 0;
+container.addEventListener('click', (e) => {
+ const found = e.target.closest('a');
+ if (found?.href) {
+ linkClicks++;
+ }
+});
+```
+
+
+
#### Example 4
Find the maximum Y coordinate while the mouse is held down
@@ -92,6 +118,31 @@ const maxY = await element
.reduce((soFar, y) => Math.max(soFar, y), 0);
```
+
+Imperative version
+
+```js
+const maxY = await new Promise((resolve) => {
+ let max = 0;
+ const mouseMoveHandler = (e) => {
+ max = Math.max(max, e.clientX);
+ };
+
+ element.addEventListener('mousemove', mouseMoveHandler);
+
+ element.addEventListener(
+ 'mouseup',
+ () => {
+ element.removeEventListener('mousemove', mouseMoveHandler);
+ resolve(max);
+ },
+ { once: true },
+ );
+});
+```
+
+
+
#### Example 5
Multiplexing a `WebSocket`, such that a subscription message is send on connection,
@@ -239,10 +290,8 @@ keys
}
})
.filter((matched) => matched)
- .subscribe({
- next: (_) => {
- console.log('Secret code matched!');
- },
+ .subscribe(() => {
+ console.log('Secret code matched!');
});
```
@@ -273,6 +322,120 @@ document.addEventListener('keydown', e => {
+#### Example 7
+
+When you mousedown on the document, it will start measuring how far your mouse moves, rendering a
+line and some text for how far you've measured, and when you mouse up, will log the final
+distance before removing the line.
+
+```js
+const measurements = document.on('mousedown').flatMap((e) => {
+ const { clientX: startX, clientY: startY } = e;
+
+ const svg = document.createElement('svg');
+ svg.width = document.body.width;
+ svg.height = document.body.height;
+ svg.innerHTML = `
+
+ 0
+ `;
+ const line = svg.querySelector('line');
+ const text = svg.querySelector('text');
+ document.body.appendChild(svg);
+ let dist = 0;
+
+ return document
+ .on('mousemove')
+ .map((e) => {
+ const { clientX: endX, clientY: endY } = e;
+ const diffX = endX - startX;
+ const diffY = endY - startY;
+ const dist = Math.sqrt(diffX ** 2 + diffY ** 2);
+ return { endX, endY, dist };
+ })
+ .takeUntil(document.on('mouseup'))
+ .do(({ endX, endY, dist }) => {
+ line.x2 = endX;
+ line.y2 = endY;
+ text.textContent = dist;
+ })
+ .reduce((_, { dist }) => dist, 0)
+ .finally(() => {
+ svg.remove();
+ });
+});
+
+const ac = new AbortController();
+measurements.subscribe(
+ (dist) => {
+ console.log(`Measured distance: ${dist}`);
+ },
+ { signal: ac.signal },
+);
+
+// Tearing everything down later
+ac.abort();
+```
+
+
+Imperative version
+
+```js
+const ac = new AbortController();
+document.addEventListener(
+ 'mousedown',
+ (e) => {
+ const { clientX: startX, clientY: startY } = e;
+
+ const svg = document.createElement('svg');
+ svg.width = document.body.width;
+ svg.height = document.body.height;
+ svg.innerHTML = `
+
+ 0
+ `;
+ const line = svg.querySelector('line');
+ const text = svg.querySelector('text');
+ document.body.appendChild(svg);
+ let dist = 0;
+
+ const mouseMoveHandler = (e) => {
+ const { clientX: endX, clientY: endY } = e;
+ const diffX = endX - startX;
+ const diffY = endY - startY;
+ dist = Math.sqrt(diffX ** 2 + diffY ** 2);
+ line.x2 = endX;
+ line.y2 = endY;
+ text.textContent = dist;
+ };
+
+ document.addEventListener('mousemove', mouseMoveHandler, {
+ signal: ac.signal,
+ });
+
+ document.addEventListener(
+ 'mouseup',
+ () => {
+ document.removeEventListener('mousemove', mouseMoveHandler);
+ console.log(`Measured distance: ${dist}`);
+ svg.remove();
+ },
+ { once: true, signal: ac.signal },
+ );
+
+ ac.signal.addEventListener('abort', () => {
+ svg.remove();
+ });
+ },
+ { signal: ac.signal },
+);
+
+// Tearing everything down later
+ac.abort();
+```
+
+
+
### The `Observable` API
Observables are first-class objects representing composable, repeated events.
@@ -465,19 +628,20 @@ synchronously emits data _during_ subscription:
```js
// An observable that synchronously emits unlimited data during subscription.
-let observable = new Observable((subscriber) => {
+const observable = new Observable((subscriber) => {
let i = 0;
while (true) {
subscriber.next(i++);
}
});
-let controller = new AbortController();
-observable.subscribe({
- next: (data) => {
+const controller = new AbortController();
+observable.subscribe(
+ (data) => {
if (data > 100) controller.abort();
- }}, {signal: controller.signal},
-});
+ },
+ { signal: controller.signal },
+);
```
#### Teardown