{"id":18879724,"url":"https://github.com/loilo/node-html-api","last_synced_at":"2025-04-14T19:23:46.927Z","repository":{"id":57267274,"uuid":"97611249","full_name":"loilo/node-html-api","owner":"loilo","description":"Makes creating an HTML API a breeze","archived":false,"fork":false,"pushed_at":"2019-02-07T20:46:25.000Z","size":186,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T07:51:10.713Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/loilo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-18T14:55:42.000Z","updated_at":"2020-05-02T21:01:31.000Z","dependencies_parsed_at":"2022-09-02T05:40:49.251Z","dependency_job_id":null,"html_url":"https://github.com/loilo/node-html-api","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loilo%2Fnode-html-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loilo%2Fnode-html-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loilo%2Fnode-html-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loilo%2Fnode-html-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/loilo","download_url":"https://codeload.github.com/loilo/node-html-api/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248943595,"owners_count":21186993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T06:39:04.277Z","updated_at":"2025-04-14T19:23:46.899Z","avatar_url":"https://github.com/loilo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HTML API\n\n[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n[![npm](https://img.shields.io/npm/v/html-api.svg)](https://www.npmjs.com/package/html-api)\n\nThis package makes it easy to give your JavaScript-generated widgets a clean, declarative [HTML API](https://www.smashingmagazine.com/2017/02/designing-html-apis/).\n\nIt features\n\n* a hybrid interface, allowing to change option values via a JavaScript API as well as by changing `data-*` attributes\n* observation of the option attributes, allowing to react to changes\n* decent presets and extensibility for type checking and casting\n* support for all modern browsers down to IE 11\n* reasonably small size: it's 3.8 KB minified \u0026 gzipped\n\n---\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTable of Contents\u003c/strong\u003e\u003c/summary\u003e\n\n* [Motivation](#motivation)\n* [Installation](#installation)\n  * [Include in the browser](#include-in-the-browser)\n  * [Include in Node.js](#include-in-nodejs)\n* [Usage](#usage)\n  * [Read and write options](#read-and-write-options)\n  * [Type constraints](#type-constraints)\n    * [Basic constraints](#basic-constraints)\n    * [Union constraints](#union-constraints)\n    * [Custom constraints](#custom-constraints)\n  * [Provide default values](#provide-default-values)\n  * [Require option attributes](#require-option-attributes)\n  * [Events](#events)\n    * [Option value changes](#option-value-changes)\n    * [New elements](#new-elements)\n    * [Error handling](#error-handling)\n* [Formal Definitions](#formal-definitions)\n\u003c/details\u003e\n\n---\n\n## Motivation\n\nThis package helps developers to provide an easy, declarative configuration interface for their component-like entities—let's call them \"widgets\"—purely via HTML.\n\nImagine an accordion widget.\n\n```html\n\u003csection id=\"my-accordion\" class=\"accordion\"\u003e\n...\n\u003c/section\u003e\n```\n\nThe way you would usually let users configure and initialize your widget on a per-instance basis is an additional inline `\u003cscript\u003e`, especially if your content is generated by something like a CMS. It may look something like this:\n\n```html\n\u003cscript\u003e\nnew Accordion('#my-accordion', {\n  swipeTime: 0.8,\n  allowMultiple: true\n})\n\u003c/script\u003e\n```\n\nThis is however inconvenient to write, a little obstrusive to read and relatively hard to maintain on the client side, especially for non-developer users.\n\nWith this package, you can use the following little block of JavaScript inside your widget\n\n```javascript\nconst api = htmlApi({\n  swipeTime: {\n    type: Number,\n    default: 0.8\n  },\n  allowMultiple: Boolean\n})('.accordion')\n```\n\nto make all accordions configurable like so:\n\n```html\n\u003csection class=\"accordion\" data-swipe-time=\"0.5\" data-allow-multiple\u003e\n...\n\u003c/section\u003e\n```\n\nThis allows users of your widget to configure it *exclusively* in HTML, without ever having to write a line of JavaScript.\n\nAt the same time, the more powerful JavaScript-side API is open to you as the widget developer, featuring many goodies explained below:\n\n```javascript\n// Access the element-level API for the first .accordion\nconst elementApi = api.for(document.querySelector('.accordion'))\n\nelementApi.options.swipeTime // 0.5\nelementApi.options.multiple // true\n```\n\n\n## Installation\n\nInstall it from npm:\n\n```bash\nnpm install --save html-api\n```\n\n### Include in the browser\n\nYou can use this package in your browser with one of the following snippets:\n\n* The most common version. Compiled to ES5, runs in all major browsers down to IE 11:\n\n  ```html\n  \u003cscript src=\"node_modules/html-api/dist/browser.min.js\"\u003e\u003c/script\u003e\n\n  \u003c!-- or from CDN: --\u003e\n\n  \u003cscript src=\"https://unpkg.com/html-api\"\u003e\u003c/script\u003e\n  ```\n\n* Not transpiled to ES5, runs in browsers that support ES2015:\n\n  ```html\n  \u003cscript src=\"node_modules/html-api/dist/browser.es2015.min.js\"\u003e\u003c/script\u003e\n\n  \u003c!-- or from CDN: --\u003e\n\n  \u003cscript src=\"https://unpkg.com/html-api/dist/browser.es2015.min.js\"\u003e\u003c/script\u003e\n  ```\n\n* If you're really living on the bleeding edge and use ES modules directly in the browser, you can `import` the package as well:\n\n  ```javascript\n  import htmlApi from \"./node_modules/html-api/dist/browser.module.min.js\"\n  ```\n\n  As opposed to the snippets above, this will not create a global `htmlApi` function.\n\n\n### Include in Node.js\n\nTo make this package part of your build chain, you can `require` it in Node:\n\n```javascript\nconst htmlApi = require('html-api')\n```\n\nIf you need this to work in Node.js v4 or below, try this instead:\n\n```javascript\nvar htmlApi = require('html-api/dist/cjs.es5')\n```\n\nNote however that the package won't work when run directly in Node since it does rely on browser features like the DOM and `MutationObserver` (for which at the time of writing no Node implementation is available).\n\n\n## Usage\n\nOnce you have somehow obtained the `htmlApi` function, you can use it to define an HTML API.\n\nLet's take a look at the most basic example with the following markup and JS code:\n\n```html\n\u003cbutton class=\"btn\" data-label=\"I'm a magic button!\"\u003e\u003c/button\u003e\n```\n\n```javascript\n/*\n * Define an HTML API with only a `label` option which must\n * be a string, and assign it to all .btn elements\n */\nhtmlApi({\n  label: {\n    type: String,\n    required: true\n  }\n})('.btn')\n\n/*\n * The `change:label` event will tell whenever the `label` option\n * changes on any `.btn`.\n * It will also trigger when the API is first applied to an element\n * to get an option's initial value.\n */\n.on('change:label', event =\u003e {\n  event.element.textContent = event.value\n})\n```\n\nThat will make our button be labeled with a cheerful `I'm a magic button!`.\n\nIf now, in any way, the button's `data-label` attribute value would be changed to `\"I'm batman.\"`, the change listener will trigger and the button label will update accordingly.\n\n[You can try out this example on Codepen.](https://codepen.io/loilo/pen/Xaromw?editors=1011)\n\n\u003e Note that, because we have set the `required` flag on the `label` option to `true`, we enforce a `data-label` attribute to always be set.\n\u003e Removing the attribute in this setup would [raise an error](#error-handling).\n\n### Read and write options\n\nOf course as a widget developer, you could get your options directly from the `data-*` attributes. However, to make use of features like type casting, you'll have to access them via the JavaScript API.\n\nLet's again take our button example from above:\n\n```javascript\nconst api = htmlApi({ label: ... })('.btn')\n```\n\nWe have now created an API, reading options from all `.btn` elements. However, to read (and write) the options of a *concrete* button element, we need to access the element-based API via the `for()` method:\n\n```javascript\nconst elementApi = api.for(document.querySelector('.btn'))\n\n// read the `label` option\nelementApi.options.label // \"I'm a magic button!\"\n\n// write the `label` option\nelementApi.options.label = \"I'm batman.\"\n```\n\n\n### Type constraints\n\nOne of the core features of this package is type casting—converting options of various types to strings, i.e. to values of `data-*` attributes (\"serialize\"), and evaluating them back to their original type (\"unserialize\").\n\n#### Basic constraints\n\nThe examples above introduced the simplest of types: `String`. However, there are many more:\n\n```javascript\nhtmlApi({\n  // This is the shorthand way to assign a type\n  myOption: Type\n})\n```\n\nInstead of `Type`, you could use one of the following:\n\n* **`null`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  `null`.\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  returning `null` if the serialized value is `\"null\"` and throwing an error otherwise.\n\n  Note that *every* option will be considered nullable if neither the definition marks it as [required](#require-option-attributes) nor it has a defined [default value](#provide-default-values).\n\n* **`Boolean`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  a boolean: `true` or `false`.\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  evaluating it as follows:\n\n  * `\"true\"` and `\"\"` (the latter being equivalent to just adding the attribute at all, as in `\u003cinput data-my-option\u003e`) will evaluate to `true`\n  * `\"false\"` and the absence of the attribute will evaluate to `false`\n\n\n* **`Number`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  of type `number`, including [`Infinity`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) but not [`NaN`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/NaN).\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  calling `+value`, which will cast a numeric string to an actual number.\n\n\n* **`Array`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  an array.\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  parsing it as JSON.\n\n\n* **`Object`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  a plain object.\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  parsing it as JSON.\n\n\n* **`Function`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  a function.\n\n  \u003e Unserializes the `data-my-option` attribute by...\n\n  `eval()`ing it.\n\n  The serialization is done via the function's `.toString()` method (which is not [yet](http://tc39.github.io/Function-prototype-toString-revision/) standardized but still works in all tested browsers so far).\n\n  Be aware that because `eval()` changes pretty much the whole environment of your function, you should only use functions that do not rely on anything but their very own parameter values.\n\n\n* **`htmlApi.Enum(string1, string2, string3, ...)`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  a string, and as such, one of the provided parameters.\n\n\n* **`htmlApi.Integer`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  an integer.\n\n  Its range can be additionally constrained by using\n\n  * `htmlApi.Integer.min(lowerBound)`\n  * `htmlApi.Integer.max(upperBound)` or\n  * `htmlApi.Integer.min(lowerBound).max(upperBound)`\n\n\n* **`htmlApi.Float`**\n\n  \u003e Enforces a value set through `elementApi.options.myOption` to be...\n\n  any finite number.\n\n  Its range can be additionally constrained by using\n\n  * `htmlApi.Float.min(lowerBound)`\n  * `htmlApi.Float.max(upperBound)` or\n  * `htmlApi.Float.min(lowerBound).max(upperBound)`\n\n\n#### Union constraints\n\nYou can use an array of multiple type constraints to make an option valid if it matches *any* of them.\n\nIf you, for example, would like to have an option for your widget that defines the `framerate` at which animations will be performed, you could do it like this:\n\n```javascript\nconst {Integer, Enum} = htmlApi\n\nhtmlApi({\n  framerate: [ Integer.min(1), Enum('max') ]\n})\n```\n\nThis would allow the `data-framerate` to either take any integer value from `1` upwards or `max`.\n\n---\n\nUnion type constraints are powerful. However, be careful when using them, especially if `String` is one of them.\n\nIf you define an option like the following:\n\n```javascript\nmyOption: [Number, String]\n```\n\nyou should be aware that the number `5` and the string `\"5\"` do serialize to the same value (which is `\"5\"`).\n\nConsequently, if you set your option's value to a numeric string (like in `api.options.myOption = \"5\"`), it will still be unserialized as the number `5`.\n\nGenerally, serialized options are evaluated from the most narrow to the widest type constraint. For example, `Number` is more narrow than `String` because all serialized numbers can be deserialized as strings, but not all serialized strings be deserialized as numbers. This means that the attempt to unserialize a stringified option value check applicable type constraints in the following order:\n\n1. [Custom type constraints](#custom-constraints)\n2. `null`\n3. `Boolean`\n4. `Number`\n5. `Array`\n6. `Object`\n7. `Function`\n8. `String`\n\nOf course, of this list, only those constraints that are given in an option's definition will be considered.\n\n\n#### Custom constraints\n\nYou can define your own type constraints. They are just plain objects with a `validate`, a `serialize` and an `unserialize` method.\n\nSince object interfaces in TypeScript are pretty concise and should be readable for most JS developers, here's the interface structure of such a constraint:\n\n```javascript\ninterface Constraint\u003cType\u003e {\n  /*\n   * Checks if a value belongs to the defined type\n   */\n  validate (value: any): value is Type\n\n  /*\n   * Converts a value of the defined Type into a string\n   */\n  serialize (value: Type): string\n\n  /*\n   * The inverse of `serialize`: Converts a string back to the\n   * defined Type. If the string does not belong to the Type\n   * this method should throw an Error.\n   */\n  unserialize (serializedValue: string): Type\n}\n```\n\nAnd since many people (me included) do learn things better by example, this is the structure of this package's built-in `Number` constraint:\n\n```javascript\n{\n  validate: value =\u003e typeof value === 'number' \u0026\u0026 !isNaN(value),\n  serialize: number =\u003e String(number),\n  unserialize: numericString =\u003e +numericString\n}\n```\n\n\n### Provide default values\n\nIf no appropriate `data-*` attribute for an option is set, its value will default to `null`.\n\nHowever, an option definition may provide a default value that will be used instead:\n\n```javascript\nconst {Enum} = htmlApi\n\nhtmlApi({\n  direction: {\n    type: Enum('forwards', 'backwards', 'auto'),\n    default: 'auto'\n  }\n})\n```\n\nNow whenever reading `elementApi.options.direction` without the `data-direction` attribute set, `\"auto\"` will be returned.\n\n\u003e **Note:** Providing a default value for an option is mutually exclusive with [marking it as `required`](#require-option-attributes).\n\n\n### Require option attributes\n\nIf an option should neither have a defined default value nor default to `null` (which could be a potential type constraint violation), you may flag it as `required`:\n\n```javascript\nconst {Enum} = htmlApi\n\nhtmlApi(btn, {\n  direction: {\n    type: Enum('forwards', 'backwards'),\n    required: true\n  }\n})\n```\n\nThis will [raise an error](#error-handling) whenever the `data-direction` attribute is not set to a valid value.\n\n\u003e **Note:** Marking an option as `required` is mutually exclusive with [providing a default value](#provide-default-values).\n\n### Events\n\nBoth the `api` (returned by `htmlApi(config)(elements)`) and the `elementApi` (returned by `api.for(element)`) are event emitters. They offer `on`, `once` and `off` methods to handle messages coming from them.\n\n#### Option value changes\n\nChanging an option, either through the `elementApi.options` interface or through a `data-*` attribute, will emit two events: `change` and `change:[optionName]`\n\nLet's say you somehow changed the previously unset option `label` to `\"Greetings, developer\"`. Then you could react to this change by using one of the following snippets:\n\n```javascript\nelementApi.on('change:label', event =\u003e {\n  /*\n   * The `event` object has the following properties:\n   */\n\n  /*\n   * \"Greetings, developer\"\n   */\n  event.value\n\n  /*\n   * null\n   */\n  event.oldValue\n\n  /*\n   * `true` if this was triggered by the initialization of the API\n   * and not by an actual change\n   */\n  event.initial\n})\n```\n\nYou could also listen to any option changes:\n```javascript\nelementApi.on('change', event =\u003e {\n  /*\n   * The `event` object has the same properties as in `change:label`\n   * and additionally:\n   */\n\n  /*\n   * \"label\"\n   */\n  event.option\n})\n```\n\nAll those events will also be propagated to the `api`. That means, you could also do:\n```javascript\napi.on('change:label', event =\u003e {\n  /*\n   * The `event` object has the same properties as in\n   * elementApi.on('change:label'), and additionally:\n   */\n\n  /*\n   * The element on which the change happened\n   */\n  event.element\n\n  /*\n   * The element API referring to that element\n   */\n  event.elementApi\n})\n```\n\nThe same goes for `api.on('change')`.\n\n\n\u003e **Note:** Please be aware that option changes will be grouped.\n\u003e That means that setting an option to two different values subsequently (i.e. in the same call stack) will only cause the last one to trigger a change with the `oldValue` on the event still being the value before the first change since the intermediate change did never apply.\n\n#### New elements\n\nIf you applied your created HTML API to a selector string instead of a concrete element, this package will set up a [MutationObserver](https://developer.mozilla.org/docs/Web/API/MutationObserver) to keep track of new elements on the website that match the selector.\n\nWhen such an item enters the site's DOM, it will trigger a `newElement` event on the `api`:\n\n```javascript\napi.on('newElement', event =\u003e {\n  /*\n   * The `event` is an object with the following properties:\n   */\n\n  /*\n   * The newly inserted element\n   */\n  event.element\n\n  /*\n   * The element API referring to that element\n   */\n  event.elementApi\n})\n```\n\n#### Error handling\n\nThe `elementApi` also emits `error` events which will be triggered when a required option is missing or an option is set to a value not matching its type constraints:\n\n```javascript\nelementApi.on('error', err =\u003e {\n  /*\n   * The `event` is an object with the following properties:\n   */\n\n  /*\n   * What caused the error\n   * \"invalid-value-js\", \"invalid-value-html\" or \"missing-required\"\n   */\n  error.type\n\n  /*\n   * Some details about the trigger\n   * Just the option name for \"missing-required\", an object in\n   * the form of { option, value } for the \"invalid-value-*\" types\n   */\n  error.details\n\n  /*\n   * A clear English message that tells what went wrong\n   */\n  error.message\n})\n```\n\nAs with the `change` events, all `error` events will be passed up to the `api` as well.\n\n\n## Formal definitions\n\nTo get a complete picture of what's possible with the `htmlApi` function, here's its signature:\n\n```javascript\nhtmlApi(options: { [option: string]: OptionDefinition|TypeConstraint }): ApiFactory\n```\n\nwhere\n\n* An `OptionDefinition` is a plain object matching the following interface:\n\n  ```javascript\n  interface OptionDefinition {\n    /*\n     * A type constraint as defined below.\n     * This *must* be set, otherwise the package will not know how\n     * to serialize and unserialize option values.\n     */\n    type: TypeConstraint\n\n    /*\n     * Tells if the data attribute belonging to this option must\n     * be set. If not set or set to `false`, the `default` option\n     * will be used.\n     */\n    required?: boolean\n\n    /*\n     * A default value, applying when the according data-* attribute\n     * is not set. If set, the option must not be `required`.\n     */\n    default?: any\n  }\n  ```\n\n* A `TypeConstraint` is either\n  * one of the following constraint shorthands:\n    * the `Boolean` constructor, allowing boolean values or\n    * the `Number` constructor, allowing numeric values or\n    * the `String` constructor, allowing strings or\n    * the `Array` constructor, allowing arrays or\n    * the `Object` constructor, allowing plain objects or\n    * the `Function` constructor, allowing functions or\n    * `null`, allowing the value to be `null`\n\n  * one of the following built-in constraints:\n    * `htmlApi.Enum(string1, string2, string3, ...)` for one-of-the-defined strings\n    * `htmlApi.Integer` for an integer number whose range might be further constrained via\n      * `htmlApi.Integer.min(lowerBound)`\n      * `htmlApi.Integer.max(upperBound)` or\n      * `htmlApi.Integer.min(lowerBound).max(upperBound)`\n    * `htmlApi.Float` for a finite number whose range might be further constrained via\n      * `htmlApi.Float.min(lowerBound)`\n      * `htmlApi.Float.max(upperBound)` or\n      * `htmlApi.Float.min(lowerBound).max(upperBound)`\n\n  * a custom type `Constraint`, which is a plain object of the following structure:\n\n    ```javascript\n    interface Constraint\u003cType\u003e {\n      /*\n       * Checks if a value is of the defined type\n       */\n      validate (value: any): value is Type\n\n      /*\n       * Converts a value of the defined Type into a string\n       */\n      serialize (value: Type): string\n\n      /*\n       * The inverse of `serialize`: Converts a string back to the\n       * defined Type. If the string can not be successfully\n       * converted to the Type, this method should throw an Error.\n       */\n      unserialize (serializedValue: string): Type\n    }\n    ```\n    This lets you easily define and use your own custom types!\n\n    or\n\n  * a union type, being a non-empty array of any of the above.\n\n    The formal way to describe this would be:\n\n    ```javascript\n    type UnionType = Array\u003c\n      typeof Boolean |\n      typeof Number |\n      typeof String |\n      typeof Boolean |\n      typeof Array |\n      typeof Object |\n      typeof Function |\n      null |\n      Constraint\u003cany\u003e\n    \u003e\n    ```\n\n    \u003e **Note:** `htmlApi.Enum`, `htmlApi.Integer` and `htmlApi.Float` are not listed in the `UnionType` definition since they are just `Constraint` objects.\n\n* An `ApiFactory` is a function which takes elements and returns an `Api` object\n\n  ```javascript\n  interface ApiFactory {\n    (elements: string|Element|Element[]|NodeList|HTMLCollection): Api\n  }\n  ```\n\n* An `Api` is a plain object of the following structure:\n\n  ```javascript\n  interface Api {\n    /*\n     * An array of all elements the API applies to\n     */\n    elements: Element[]\n\n    /*\n     * Gets the element-based API for a certain element\n     */\n    for (element: Element): ElementApi\n\n    /*\n     * Adds a listener to the `change` or `error` event\n     */\n    on (\n      event: \"change\",\n      listener: (event: OptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    on (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    on (\n      event: \"error\",\n      listener: (event: ErrorEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n\n    /*\n     * Like `on`, but listeners detach themselves after first use\n     */\n    once (\n      event: \"change\",\n      listener: (event: OptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    once (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    once (\n      event: \"error\",\n      listener: (event: ErrorEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n\n    /*\n     * Removes listeners\n     */\n    off (\n      event: \"change\",\n      listener: (event: OptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    off (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n    off (\n      event: \"error\",\n      listener: (event: ErrorEvent \u0026 ElementRelatedEvent) =\u003e any\n    ): this\n\n    /*\n     * Destroys the API, disconnecting all MutationObservers\n     * Also destroys all ElementApi objects\n     */\n    destroy (): void\n  }\n  ```\n\n* `ElementApi` is a plain object of the following structure:\n\n  ```javascript\n  interface ElementApi {\n    /*\n     * An object with all defined options as properties\n     */\n    options: { [option: string]: any }\n\n    /*\n     * Adds a listener to the `change` or `error` event\n     */\n    on (\n      event: \"change\",\n      listener: (event: OptionChangeEvent) =\u003e any\n    ): this\n    on (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent) =\u003e any\n    ): this\n    on (\n      event: \"error\",\n      listener: (event: ErrorEvent) =\u003e any\n    ): this\n\n    /*\n     * Like `on`, but listeners detach themselves after first use\n     */\n    once (\n      event: \"change\",\n      listener: (event: OptionChangeEvent) =\u003e any\n    ): this\n    once (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent) =\u003e any\n    ): this\n    once (\n      event: \"error\",\n      listener: (event: ErrorEvent) =\u003e any\n    ): this\n\n    /*\n     * Removes listeners\n     */\n    off (\n      event: \"change\",\n      listener: (event: OptionChangeEvent) =\u003e any\n    ): this\n    off (\n      event: \"change:[optionName]\",\n      listener: (event: ConcreteOptionChangeEvent) =\u003e any\n    ): this\n    off (\n      event: \"error\",\n      listener: (event: ErrorEvent) =\u003e any\n    ): this\n\n    /*\n     * Destroys the API, disconnecting all MutationObservers\n     */\n    destroy (): void\n  }\n  ```\n\n* There are several kinds of events mentioned above. They are all plain objects with different structure:\n\n  ```javascript\n  interface ElementRelatedEvent {\n    /*\n     * The element the event refers to\n     */\n    element: Element,\n\n    /*\n     * The ElementApi for the given element\n     */\n    elementApi: ElementApi\n  }\n\n\n  interface OptionChangeEvent {\n    /*\n     * The new value of the option\n     */\n    value: any,\n\n    /*\n     * The option's previous value\n     */\n    oldValue: any\n  }\n\n\n  interface ConcreteOptionChangeEvent extends OptionChangeEvent {\n    /*\n     * The name of the changed option\n     */\n    option: string\n  }\n\n\n  interface ErrorEvent {\n    type:\n      \"missing-required\" |\n      \"invalid-value-js\" |\n      \"invalid-value-html\"\n\n    /*\n     * A clear, English error message\n     */\n    message: string\n\n    /*\n     * Any details on the error\n     */\n    details: any\n  }\n  ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floilo%2Fnode-html-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floilo%2Fnode-html-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floilo%2Fnode-html-api/lists"}