{"id":15984677,"url":"https://github.com/aappddeevv/scala-vdom","last_synced_at":"2025-07-09T04:34:29.380Z","repository":{"id":34685692,"uuid":"38659856","full_name":"aappddeevv/scala-vdom","owner":"aappddeevv","description":"Virtual DOM for scala.","archived":false,"fork":false,"pushed_at":"2018-03-15T23:37:47.000Z","size":205,"stargazers_count":19,"open_issues_count":1,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-02T11:36:26.783Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aappddeevv.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":"2015-07-07T02:49:47.000Z","updated_at":"2019-07-24T13:13:32.000Z","dependencies_parsed_at":"2022-09-14T18:11:49.121Z","dependency_job_id":null,"html_url":"https://github.com/aappddeevv/scala-vdom","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aappddeevv/scala-vdom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aappddeevv%2Fscala-vdom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aappddeevv%2Fscala-vdom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aappddeevv%2Fscala-vdom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aappddeevv%2Fscala-vdom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aappddeevv","download_url":"https://codeload.github.com/aappddeevv/scala-vdom/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aappddeevv%2Fscala-vdom/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264394606,"owners_count":23601336,"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-10-08T02:10:06.594Z","updated_at":"2025-07-09T04:34:29.346Z","avatar_url":"https://github.com/aappddeevv.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# scala-vdom\n\nscala-vdom is a simple virtual DOM written entirely in scala. It is based heavily on virtual-dom\n and other virtual DOM implementations. The library is a low-level library and not intended to\nbe used by web developers directly but to be used by other libraries.\n\nThis library is currently a proof of concept. See [Issues](#issues).\n\nThis virtual DOM implementation is designed to support:\n\n* HTML5\n* Client (js, jvm) and server (js, jvm) side\n* Asynchronous processing, where possible.\n* Browser API standardization\n\n\nThe implementation approach allows you to use scala-vdom in multiple ways. For example,\nyou can use it as a traditional virtual dom with diffing the full tree or you can use our \nown library to create patches and stream them to a node (using IOActions).\n\nscala-vdom is designed to have multiple, replaceable layers:\n\n* VDom: The virtual DOM layer that creates \"nodes\" that are eventually rendered into a\nBackend specific UI widget set, such as the browser DOM.\n* Patch: A recipe that describes how to change one VDom into another VDom. The recipe\nsometimes uses a VDom instance to conveniently collect together the arguments for\ndescribing the change but it is conceptually independent on the VDom layer.\n* IOAction: A monad that executes side effects. Applying a Patch to a Backend specific\nobject is considered a side-effect. In DOM speak, patching a DOM that removes then\ninserts a new DOM element is considered a side-effect. You can build up a list\nof actions to apply to the DOM then apply them all at once.\n* Backend: A UI environment specific object that knows how to render a VNode, take\na patch and create a side-effecting action specific to a UI environment. It also\nknows how to run an IOAction.\n\nThe layers have been created to allow scala-vdom to execute efficiently in multiple \nenvironments. scala-vdom was designed to run on clients and servers in both scalajs\nand non-scalajs environments. Most of the core layers create immutable objects that\nare Backend independent or at least highly decoupled from a Backend.\n\nTo create the layers, a slick-like Backend was created. The Backend \"lifts\" the Patch\nand VNode objects into it using implicits and transforms those fairly simple objects\ninto executable actions. This design approach makes extensions more difficult to implement\nbut it reduces the burden on the programmer by providing a much simpler set of classes\nand type signatures to build on.\n\n## VNode\nVirtual DOM trees are built using the VNode classes. VNode objects are immutable. Use of \nVNode is entirely optional. You could generate the patches directly yourself.\n\nVNode trees can be created in multiple ways:\n\n* Creating the VNode tree using a function you define. The function may use\napplication specific logic to customize the VDom tree to reflect changes in application state.\n* Creating a single VNode tree then using a lens (shapeless, scalaz, monacle) to mutate\nthe tree.\n* Using a more friendly API such as scalatags. Scalatags needs to be configured to\ngenerate VDom objects instead of text or DOM objects.\n* Use a ThinkNode (a VNode subclass) which allows you to generate VNodes from within\nthe tree rather than external to it.\n\nThe VNode class hierarchy is sealed and cannot be extended.\n\n## Patching\nPatching is the process of updating a DOM node. The updates can be calculated in a number\nof different ways depending on your application.\n\n* Through the `diff(original, target)` method. The diff method applies an \nexternal algorithm to create a Patch. The algorithm narrows down\nthe patch creation process so that a VNode's `diff` method is called on objects\nthat are of the same type. Each VNode knows how to `diff` with an object like itself\nmuch in the same way that it can determine if it is equal to another object of the same\ntype.\n* By creating the patches directly yourself based on your knowledge of which updates are\nneeded.\n* By creating the patches directly yourself and sending them from the server to the client\nwhere they are interpreted and applied to the DOM. You could for example, use uPickle.\n* Skip the VNode interface completely, and generate your own patches based on your own\nvirtual node concept.\n* Using a scala lens (such as those in shapeless, scalaz or monacle) to change a single\nelement in a VDom tree and then calling `diff`.\n* Receiving a patch then using the IOAction combinators to continue to author your\nown DOM mutation function calls directly on the DOM elements contained in the IOAction monad. This approach is not recommended of course. \n\n## Diffing\nDiffing occurs at two levels.\n\nThe first level is to compare VNodes and find out if tree-level changes are needed. For\nexample, if there is no node but a node is being asked to be created, then the first level\nof diff computation issues a patch to create a node.\n\nThe second level is at the node level. The diff algorithm will run a node level diff\nif the VNodes are the same type. Each VNode knows how to diff with an object of the\nsame class but not with objects of another VNode class. This allows the diff logic to\nbe concentrated in the VNode class.\n\nThe diff algorithm tries to find the smallest and/or most efficient set of Patches\nto mutate the original VDom into the target VDome.\n\nCurrently, there are no optimizations. The entire tree is re-created :-) Ouch!\n\nBelow are my notes for looking into diffing optimizations:\n\n* Use of the key.\n* Use of fancy tree changes searching algorithms.\n* Use of of explicit \"no changes here\" tags in VNode.\n* Use of ThunkVNode.\n* Lenses (you can use these now for VNode mutation) that point out where changes \"might\" occur. In other words, given a big tree, \"here's\" the places in the big tree to look for\nchanges.\n\n## Setting Attributes \u0026 Properties\n\nMost vdom libraries allow you to set attributes and properties on a javascript object. Technically\nall values that can be specified on an element are either approved attributes (like `id`) or \ncustomer attributes specified by `aria-` or `data-`. Other key-value pairs are not technically\nallowed as attributes in HTML, specifically the markup. However, when programming the DOM using\njavascript, some attribuse are set when their\nobject properties are set e.g. `myHTMLElement[id] = 'foo'` actually sets the `id` attribute\nas if `myHTMLElement.setAttribute('id', 'foo')` had been called.\n\nIn the object representation of the DOM used when programming with javascript, many\nlibraries specify properties on the objects. These properties either assist the UI\nprogramming design of a specific library or are set using index notation as a short-hand instead\nof using the `setAttribute` methods. The HTML specs actually suggest using the index notation\ninstead of `setAttribute` for some attributes to improve cross-browser compatibility. \n\n## Node Queues\nInstead of making a library specific hack around cleaning up the DOM when the DOM\nis updated (and elements potentially removed), scala-vdom has a cleanup queue\nthat is added to each node that holds IOActions that are run when cleanup\nis requested by scala-vdom. You can use the same mechanism to add hooks that\nare run when attribute values are changed or a DOM node is removed from the DOM.\nReact essentially has these but it was very hard to figure this out from the React\ncode.\n\n## Handling Events\n\nHandling events smartly is currently a research issue. It seems that attaching\ndirectly to a DOM object may be Ok but some virtual dom libraries attach to the\ntop of the tree and manage events throughout the tree.\n\nThere are multiple ways to handle events.\n\n* You can use a delegate model\n  with the ability to manage events that occur further down in the tree.\n  To use this approach you need a way to \"select\" when a handler is fired\n  from a sub-node. This event handling model helps improve performance in\n  some cases and hurts performance in others.\n* Or you can attach directly to a node and have a callback per event type\n  as needed.\n* Or you can lift all events into an event queue and wrap all events like\n  React or do some type of event to semantic action translation.\n* or you could Object.observe which may or may not be helpful in the long\nrun.\n \nIt appears that there are advantages for each approach and there\nis evidence that for some events, like \"error\", you need to attach\ndirectly to the element that generates the error because the \"error\"\nevent type does not bubble. In other words, to smooth over the DOM event\nhandling bumps, you have a lot of work ahead of you.\n\nThere is also the issue of debouncing, where multiple similar events\nare compressed into one in order to avoid race conditions between\nevents and UI activity.\n\nFor the time being, I'll just copy the concepts from [ftdomdelegate](https://github.com/ftlabs/ftdomdelegate)\nexcept make \"delegate module\" objects immutable. There may be a sprinkling of influence from\n[jsaction](https://github.com/google/jsaction.git). Like jaction, it would\nbe nice to make this all string oriented with a dynamic dispatch\nunderneath--to help server side rendered pages load faster. And it would be\nnice to move to semantic actions versus raw event processing. Another lib is onoff although\nit is older [onoff](https://github.com/LiftoffSoftware/OnOff). EventEmitter (from node.js\nbut ported to the browser) is another delegate-like library. [dom-delegator](https://github.com/Raynos/dom-delegator)\nis another DOM event delegation library. They are all about the same--mutable, non-reactive.\n\nI'll look into reactive solutions like Li's rxscala, however, it is not clear that it will \nwork easily in a virtual DOM because of the virtual layer versus using rxscala in the layer\nabove scala-vdom.\n\nIt is all very inconsistent.\n\nscala-vdom comes with a port to pure scalajs of ftdomdelegate. It can be used independently\nof the vdom package.\n\n\n## Toy Example\n\nAssume that `test7` is an id in your DOM where you want the toy example to render into:\n\n```scala\n    //\n    // Expanding box\n    //\n     val target7 = document.getElementById(\"test7\")\n\n    def box(count: Int) = VNode(\"div\", Some(\"box\"),\n      Seq(textAlign := \"center\", lineHeight := s\"${100 + count}px\",\n        border := \"1px solid red\", width := s\"${100 + count}px\", height := s\"${100 + count}px\"),\n      VNode(count.toString))\n\n    var count = 0\n    var tree = box(count)\n    var rootNode = DOMBackend.render(tree)\n    rootNode.foreach(target7.appendChild(_)) // manual append\n\n    val cancel = setInterval(1000) {\n      count += 1\n      val newTree = box(count)\n      val patch = diff(tree, newTree)\n      rootNode.flatMap { n =\u003e DOMBackend.run(patch(n)) }\n      tree = newTree\n    }\n    setTimeout(10 seconds)(clearInterval(cancel))\n```\n\n## Hooks or the Lack Thereof\nVirtual-dom contains hooks that run after a VNode has been turned into a DOM element. Many other libraries have\nsomething similar.\n\nBecause we want to keep the Patch and VNode objects as simple as possible, hooks can be specified and used\nby the Backend if a Backend enables some kind of hook mechanism. Having said that, it is much easier\nto use the IOActions, which are monads, and just append your post-creation behavior as monadic computation.\n\nThe layer that sits on top of scala-vdom may integrate hooks much more tightly into its syntax and then\nflow the hooks into the IOActions via monadic computations.\n\n## Browser Support\n\nIt is known that this does not support IE8, too many exceptions and issues with Internet Explorer. It's possible that more modern versions of IE may work Ok. Over time, we may be able to provide better support to various generations of IE, but it appears to be very difficult to do so.\n\n## Issues\nThere are many issues that currently make this library less than ideal for use. These \nissues will be resolved in future releases.\n\n* There are very few optimizations in the diff'ing algorithm. It's a straight diff of the entire tree.\n* Adjacent VText nodes have bad behavior when rendered in the DOM. Browsers merge text nodes\ntogether in some cases. The general rule is to avoid adjacent text nodes in your virtual dom.\n* Support is provided for events through a Delegate procedure, but I would like to improve it.\n* The presence of the correct ExceutionContext still needs to be traced and worked out so that\nit can always be specified by the programmer.\n* Should diff'ing and rendering have Future return values to allow them to be async by default?\nNot sure this makes sense in every backend environment. Can't the programmer just wrap it into\na Future themselves if they want it async? There *are* side effects for rendering, potentially,\nbut probably not diff'ing.\n* I've not taken the time to add more attributes to the set and adding your own attributes\nwith custom hints is not easy at the moment, you have to create your own Backend object.\n* Server side rendering. While most vdom libraries other than react do not support this,\nI think its important for a variety of reasons.\n\nSo you can use this library as way to construct your application but you will probably\nstill have to do some workaround where the library is weak and no support is provided for\nautomatic redraw in the spirit of a component approach. I started a component \nsub-project but have not had time to work on it.\n\n## Other Virtual DOM implementations\nHere's a list of virtual dom implementations that I looked at:\n\n* [react](https://facebook.github.io/react/): Facebook's well known version\n* [diffDom](https://github.com/fiduswriter/diffDOM): js\n* [virtual-dom](https://github.com/Matt-Esch/virtual-dom): js\n* [incremental-dom](https://github.com/google/incremental-dom): js, essentially reproduces a XAML-ish type environment. From google.\n* [dom-layer](https://www.npmjs.com/package/dom-layer): js\n* [ember / glimmer](https://github.com/emberjs/ember.js/pull/10501): js, have not looked at this quite yet\n* [citojs](https://github.com/joelrich/citojs): js\n* [cycle.js](https://github.com/cyclejs)\n\nA number of libraries sit on top of these virtual DOM implementations. I need to look at these as well to understand how layers might be built above this vdom implementation and what is needed in this layer to facilitate easy adoption.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faappddeevv%2Fscala-vdom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faappddeevv%2Fscala-vdom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faappddeevv%2Fscala-vdom/lists"}