npm package: @hoge1e3/dom-updater
Description: A DOM tree updater that efficiently updates one HTML element tree (`dst`) to match another (`src`), with hooks to customize update behavior.
Author: hoge1e3
Latest version: 1.0.1

# `@hoge1e3/dom-updater`

A DOM tree updater that efficiently updates one HTML element tree (`dst`) to match another (`src`), with hooks to customize update behavior.

## Installation

```bash
npm install @hoge1e3/dom-updater
```

## Usage

```ts
import { updater } from "@hoge1e3/dom-updater";

const domUpdater = updater({
  // Optional hooks
});

domUpdater.update(destinationElement, sourceElement);
```

## API

### `updater(opt: Options): { update(dst: HTMLElement, src: HTMLElement): void }`

Creates an updater instance with the given options. Returns an object with an `update` method to perform the DOM update.

#### Parameters

- `opt` (optional): An `Options` object to customize update behavior.

#### Returns

An object with:
- `update(dst, src)`: Updates `dst` (destination DOM) to match `src` (source DOM).

---

## `Options` (Type)

| Property        | Type                                         | Description |
|-----------------|----------------------------------------------|-------------|
| `findUpdatable` | `(src: Node) => ChildNode \| null` | Custom logic to locate an updatable node from rendered document corresponding to `src`. Default is "find the node from rendered document having same id of `src`".  |
| `isUpdatable`   | `(dst: Node, src: Node) => boolean`           | Determines if a pair of nodes can be updated in-place. If `dst=findUpdatable(src)`, it must be `isUpdatable(dst, src)`,  Default is either "Both `dst` and `src` have same id". "They have no id and have same tag name", or "Both of them are text nodes". |
| `isSkippable`   | `(dst: Node, src: Node) => boolean`           | Determines if the update for a node can be skipped. Default is "having same id and same value of attribute whose name is specified by `attr_version` option". |
| `attr_version`  | `string`                                     | Attribute used to determine if nodes can skip updates (defaults to `"data-version"`). |
| `props`         | `(node: Node) => string[]`                    | List of properties to synchronize (Default: `value` and `checked`). |

---

## How "updating" works
update(`dst`, `src`) works as:
1. **Shallow Update**: Node attributes and properties are updated first.
2. **Recursive update**: Child nodes are updated recursively:
   - Call `isUpdatable` on `dst` and `src` subnodes in that order to check if they are updatable.
   - If not, call `findUpdatable` passing `src` subnode to find updatable nodes.
   - If an updatable node is found in `dst`:
      - If necessary, reorder the updatable nodes in the order of `src`.
      - If `isSkippable` is called and the result is true, skip updating the versioned element.
      - If not skippable, do a recursive **Recursive Update**.
   - If no updateable node is found, insert a subnode of the `src` node into `dst`.
3. **Delete Unused Nodes**: Nodes in `dst` that were not updated in step 2 are unused, and they are deleted.

---

## Example

```ts
const dst = document.getElementById("app")!;
const src = document.createElement("div");
src.innerHTML = `
  <div id="foo" data-version="1">Updated Content</div>
`;

const domUpdater = updater({
  attr_version: "data-version",
});

domUpdater.update(dst, src);
```

---

## Notes

- Text nodes are updated based on `textContent`.
- Attribute differences are synced.
- DOM order is preserved, with optimizations for minimal DOM operations.
- Unused nodes are removed after update.

## Future works
- In this version, event listeners are not updated (event listeners are retained as they were when first inserted). 
- In the near future, we plan to allow event listeners to be updated as well, but there will be restrictions on how event listeners can be registered (we will design this assuming [@hoge1e3/dom](https://www.npmjs.com/package/@hoge1e3/dom) is used)