npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

xtal-deco

v0.0.39

Published

Add properties / methods to other DOM (custom) elements.

Readme

<xtal-deco>

Proxy neighboring DOM (custom) element.

Adding behavior to the next element instance with xtal-deco

xtal-deco provides a base class for adding behavior to the next sibling element -- "decorating" the element, via an ES6 proxy.

It is inspired by decorators/annotations/attributes. Rather than declaratively applying to a class or class member beneath the decorator/annotation/attribute, an xtal-deco (or xtal-deco derived elements) instance applies behavior/functionality to the native or custom DOM element beneath it.

xtal-deco is one member of a trio of related elements, provided as an alternative to the controversial built-in native element extension.

xtal-deco has a property, "actions" that allows for a "reactive" way of responding to property changes passed through via the proxy.

actions is an array of arrow functions, where the proxy of the target element is passed in. It is expected that the arrow function will use destructuring:

actions: [
    ({textContent, myProp}) =>{
        //do something
    }
]

The other key properties of xtal-deco are:

  1. on -- This is where we attach event listeners on the target element.
  2. init -- This is where we initialize the behavior.
  3. finale -- this is where we clean up when the decorated element is set free from the DOM tree.

All four properties -- actions, on, init, finale, are required to be defined for the decoration to take place (even if they don't actually do anything).

Setting proxyActions

There are two straightforward ways of setting the actions/on/init, depending on your needs:

  1. Extend class XtalDeco, and specify the actions/on/init properties in the constructor or via instanceFields.
  2. Inline actions in the HTML markup. Use something like nomodule.

Syntax example for approach 2:

<xtal-deco><script nomodule=ish>
    const decoProps = {
        actions: [
            ({count, self}) => {
                window.alert(count + " butterbeers sold");
            }
        ],
        on: {
            click: ({self}) => {
                self.count++;
            }
        },
        init: ({self}) => {
            self.count = 0;
        },
        finale:({self}) => {
            console.log("Good bye");
        },
    }
    Object.assign(selfish.parentElement, decoProps);
</script></xtal-deco>
<button disabled data-drink-selection="Butterbeer">Click me to Order Your Drink</button>
<!-- p-d is short for "pass-down" -->
<p-d observe=xtal-deco on="count-changed" to=[-text-content] val=detail.value></p-d>
<span></span> drinks sold.

All property changes cause an event to fire with the lisp-case name of the property followed by -changed. So in the example above, when the "count" property changes, the decorator element (xtal-deco) fires and event "count-changed."

NB I: Here we are, via a proxy, setting a field value on an existing native DOM element -- button in this case.

"Throwing new properties" on another DOM element is considered problematic. Consequently, this web component has a protective curse -- anyone trying to add a new property or a method onto another element will receive a one-way ticket to Azkaban.

However, the ability to add new data elements is critical when enhancing behavior. We need the ability to add new properties onto our proxy only.

To do this, use property/attribute virtualProps/virtual-props, which allows for an array of string properties to be specified:

<xtal-deco virtual-props='["count", "revenue"]'></xtal-deco>

Doing so causes the properties "count" and "revenue" to be stored and retrieved via a WeakMap.

Why not wrap the element?

Why not:

<xtal-deco>
    <button>...</button>
</xtal-deco>

?

Explanation can be found here.

Recursive Tree Structures

For recursive tree structures, you can, in addition to the next sibling, target children of the target element via the whereTargetSelector/where-target-selector property / attribute.

Closest match

If multiple decorators are needed on the same element, then at least one of the decorators will need to skip over the other decorators. You can specify which element to target from the set of nextElementSiblings via the matchClosest / match-closest property/attribute.

Externally apply properties to the target element via the proxy.

xtal-deco creates a WeakMap property, targetToProxyMap, which allows us retrieve the proxy from the target DOM element. We can then set property values programmatically.

However, this is rather clumsy in practice.

Another way to handle this is to define properties that need passing from external sources on the custom element that extends xtal-deco. The property setter would need to forward the value on to the proxy / proxies via the "mainProxy" property of the base class.

Running locally

  1. Install node.js
  2. Fork and/or clone https://github.com/bahrus/xtal-deco
  3. Open a command window to the fork or clone directory on your local machine
$ npm install
$ npm run serve

Now open a browser to http://localhost:3030/demo/dev.html