A library to implement directive pattern for native HTML without any framework, which is inspired by Vue.js.
See DEMO
For a long time we've relied on approaches where inserting a Web Component into HTML activates functionality immediately, or where frameworks like Vue and Angular use directives to extend HTML elements. Plain HTML doesn't have a directive-like functions that lets us inject JavaScript behaviors just by adding an attribute.
For example, if you want a copy to clipboard feature that works across projects and environments, the traditional way is to write JS that binds a click event to the button:
This works in static HTML, but it can fail with virtual DOM frameworks like Vue or React because the virtual DOM may rewrite the DOM tree and remove event bindings.
To make small features reusable across projects and environments, a common solution is to use delegated event listeners:
This approach has some drawbacks. First, the delegate pattern is not commonly used for developers unfamiliar with jQuery or similar libraries and may cause confused. Second, for SPAs that frequently need to unbind event handlers, this method can lead to memory leaks or unexpected behavior because the listeners remain attached to the DOM even after it have been removed.
Another solution is implement a CustomElement such as <copy-button>, which ensures it works everywhere.
However, Web Components have a higher development barrier: adding a custom HTML element for a simple feature can feel heavy or unintuitive, and enabling Shadow DOM may make CSS harder to manage.
WebDirective aims to let developers easily write cross-project, cross-environment HTML extensions that can be mounted to existing HTML with non-invasively and removed cleanly without side effects or leftovers. The example below shows implementing a copy to clipboard feature as a directive and mounting it in a Vue environment:
As an early experimental version, we heavily referenced Vue.js for the interface to reduce the learning curve for developers. However, due to some limitations of native HTML, we cannot achieve exactly the same behavior.
But so far, a simple plug-and-play, non-invasive, side-effect-free directive system is already very useful for building standalone widgets that work across environments and frameworks. Implementations of directives like this have been used in our team since 2020, it is very stable and intuitive, and perfectly suitable for production use.
NPM or Yarn
UnPkg
Bundler
Browser
Listen to smaller scope.
Stop listening
After register a directive (for example: foo), you can add w-foo directive to any HTML element and the directive will instantly work.
This is very useful that if you want to add some cross-platform custom logic to existing Vue/React/Angular template without writing code for every frameworks.
Now, add w-foo to HTML, it will run the mounted() hook:
The binding interface:
Use JSON as value
WebDirective provides a static method to get singleton instance.
WebDirective provides a useEventListener() helper to help you listen to events and auto unbind them when element unmounted.
Note if you use async function as mounted hook, you must all useEventListener() before first await.
By default, unlike Vue.js, WebDirective's updated hook only listen to the updates of element itself. If you want to listen to children elements' changes, you can set the enableChildrenUpdated option to true. Different from Vue.js, you must use childrenUpdated hook to handle children elements' updates.
WebDirective supports argument and modifiers like Vue.js. However, due to native HTML not supports query elements by dynamic attribute names, this function must traverse elements to find attributes, which is not very efficient, so it is disabled by default, you must manually enable it.
Now you can add directive like this:
And you can access the argument and modifiers in the binding object:
When enable argument and modifiers, you can add multiple directives to one element:
All modifiers are boolean values, if a modifier exists, its value is true, otherwise wll not exists. Since HTML attributes not supports camelCase, all modifier must write as kebab-case, and will auto convert to camelCase after parsed.
Important
Native HTML do not support to change argument and modifiers dynamically, if you change them, it will be same as removed the old attribute and re-add a new attribute, so the unmounted and mounted hooks will be called.
All hooks list below:
- mounted(el, binding): Called when the directive is first bound to the element. This is where you can set up any initial state or event listeners.
- unmounted(el, binding): Called when the directive is unbound from the element. This is where you can clean up any resources or event listeners.
- updated(el, binding): Called when the value of the directive changes. This is where you can respond to changes in the directive's value.
- childrenUpdated(el, binding): Called when the children elements of the element are updated. This hook is only available when enableChildrenUpdated option is set to true.
Note
Unlike Vue.js, all hooks will be called after mutation occurs, at this time, the DOM is already updated. So WebDirective do not provide beforeMount, beforeUpdate and beforeUnmount hooks.
WebDirective uses MutationObserver to listen to DOM changes, so the updated hook will be called after the mutation occurs. If you want to get the result after directive updated, you must wait next event loop.
WebDirective provides a static method nextTick() to wait for next update cycle.
WebDirective can emit custom events when directives are mounted, unmounted, or updated.
By default, the event names are prefixed with wd:. You can listen to these events on the element:
- wd:mounted
- wd:unmounted
- wd:updated
- wd:children-updated
If you want to change the event prefix, you can set the eventPrefix option when creating WebDirective instance.
You can use custom prefix to avoid conflicts with other libraries.
List of all options as table
| prefix | string | w- | The prefix for directive attributes. |
| enableAttrParams | boolean | false | Enable argument and modifiers support. |
| enableChildrenUpdated | boolean | false | Enable children elements update listening. |
| eventPrefix | string | wd: | The prefix for custom events emitted. |
.png)

