{"id":13726405,"url":"https://github.com/developit/unified-element-properties-proposal","last_synced_at":"2025-04-14T08:11:37.911Z","repository":{"id":66020119,"uuid":"271567572","full_name":"developit/unified-element-properties-proposal","owner":"developit","description":"Unified Element Properties for the DOM","archived":false,"fork":false,"pushed_at":"2020-06-11T14:43:28.000Z","size":5,"stargazers_count":58,"open_issues_count":0,"forks_count":0,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-10T11:16:12.206Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/developit.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-06-11T14:27:27.000Z","updated_at":"2021-12-24T11:00:08.000Z","dependencies_parsed_at":"2023-03-10T23:29:28.265Z","dependency_job_id":null,"html_url":"https://github.com/developit/unified-element-properties-proposal","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Funified-element-properties-proposal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Funified-element-properties-proposal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Funified-element-properties-proposal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/developit%2Funified-element-properties-proposal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/developit","download_url":"https://codeload.github.com/developit/unified-element-properties-proposal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248843874,"owners_count":21170494,"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-08-03T01:03:03.364Z","updated_at":"2025-04-14T08:11:37.879Z","avatar_url":"https://github.com/developit.png","language":null,"readme":"\n# Proposal:  Unified Element Properties\n\nToday's UI frameworks exert considerable effort attempting to reconcile or \"paper over\" the many differences\nbetween Element properties and attributes.  In React, this is manifested as the abstract \"props\" concept which,\nwhen applied to DOM Elements requires a [complex mapping](https://github.com/facebook/react/blob/master/packages/react-dom/src/shared/DOMProperty.js)\nof abstract prop names to corresponding DOM properties or attributes.\nIn Preact, an \"in test\" (prop in node) is used to infer whether a given abstract \"prop\" should be applied to the\nDOM as a property or an attribute. This approach handles Custom Element properties more naturally, but it comes at\nthe cost of some performance and determinism.  Other rendering libraries choose to expose the decision of whether to\nset a given value as a property or an attribute to the developer - Vue uses prefixes (:prop=\"value\") to explicitly\ncontrol this.\n\nIt seems a set of new DOM methods designed specifically to address this case could be impactful. These methods would\nestablish a consistent approach for setting properties/attributes based on the given \"virtual property name\".\nIf widely adopted, this would lead to better interoperability between rendering libraries.  It also opens the door for\neasier Custom Element upgrades through the use of overridden methods, and potentially provides a starting point for\naddressing the attributes → properties upgrade issue faced by today's components.\n\n### How is this different from setting DOM properties?\n\nSetting a DOM (reflected) property immediately triggers any side effects associated with that property's setter.\nIn the DOM, this can be surprisingly expensive - assigning to the src property of an `\u003cimg\u003e` element causes it\nto be loaded, even if the value assigned is identical to the current value. This is true for most DOM properties,\nincluding all properties governing Node contents (`.data`, `.textContent`, etc). This is mostly intuitive when working\ndirectly with the DOM's imperative API, but has resulted in all modern frameworks implementing what are effectively\ncaches around DOM property access in order to achieve reasonable performance. Furthermore, both reading and writing\nDOM properties incurs the cost of a binding traversal, since element behaviors are not implemented in JavaScript.\n\nOrdering of property assignment can also be tricky to get right. Similar to the `\u003cimg\u003e` issue noted above, assigning\nto the `.src` property of an Image and then immediately assigning to the `.crossOrigin` property will cancel the\nalready-started request and re-issue it with updated CORS constraints (note: this might be a browser bug).\n\nSimilar ordering concerns are present for many DOM properties - setting an Node's contents via `.data` or `.textContent`,\nneeds to happen prior to setting properties that refer to that content like `.datalist` and `.selected`. The behavior of\nattributes can sometimes be more manageable in this regard, since attribute changes are batched. Synchronous code that\nmodifies attributes generally behaves the same regardless of the order in which they are set, even across multiple elements.\n\n## API Sketch\n\n```webidl\ninterface Element {\n  void setProperty(string propertyName, any value);\n  any getProperty(string propertyName);\n}\n```\n\n#### Example usage:\n\n```js\nconst element = document.createElement('div');\nelement.setProperty('class', 'demo');\ndocument.body.appendChild(element);\nelement.getProperty('class');  // \"demo\"\nelement.setProperty('class', 'demo');  // ignored - value is unchanged\n```\n\n# Additional Opportunities\n\n## Referential Equality\n\nThis would also open up an interesting possibility for simplifying how the DOM is manipulated both by developers\nand through popular libraries, by providing a method to apply a set of properties to an Element where properties\nwith referentially-equal (or semantically equal?) values are skipped:\n\n```webidl\ninterface Element {\n  void setProperty(string propertyName, any value);\n  any getProperty(string propertyName);\n  void setProperties(map\u003cstring, any\u003e; properties);\n}\n```\n\n#### Example usage:\n\n```js\nconst element = document.createElement('div');\nelement.setProperties({\n  class: 'demo',\n  id: 'demo-1'\n});\ndocument.body.appendChild(element);\nelement.setProperties({\n  class: 'demo',  // ignored - value is unchanged\n  style: { backgroundColor: 'red' }\n});\n```\n\n## Observable Bindings\n\nGiven the existence of a `setProperty()` method on DOM Nodes, it becomes possible to implement automatic one-way\ndata binding simply by allowing developers to pass an [Observable](https://github.com/surma/observables-with-streams)\nas a value. When given an Observable, setProperty obtains the current value of the Observable and applies it as it\nwould any static value, then subscribes to the observable. Future values propagated through the observable are\nreflected automatically as if setProperty had been invoked with the new value.\n\n```js\nconst element = document.createElement('h1');\nconst title = new Observable('initial title');\nelement.setProperty('title', title);  // assigns h1.title to \"initial title\"\ndocument.body.appendChild(element);\ntitle.next('updated');  // assigns h1.title to \"updated\"\n```\n","funding_links":[],"categories":["Others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevelopit%2Funified-element-properties-proposal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevelopit%2Funified-element-properties-proposal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevelopit%2Funified-element-properties-proposal/lists"}