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 🙏

© 2024 – Pkg Stats / Ryan Hefner

modular-behaviour.js

v3.2.1

Published

Attach behaviours to your html nodes

Downloads

15

Readme

modular-behaviour.js

NPM Downloads

Bind js behaviours (js classes, functions, jquery plugins...) to your dom nodes.

NOTE: the v3 contains significant changes over the previous versions. Check branch 2 if you want to use the previous stuff.

What does this library solve ?

This library act as a glue between your js libraries and your html document. It will allow to initialize most things out of the box by using a custom element. The V3 use a custom element because it's self initializing which is a big advantage over watching the dom for changes. You basically get "watching the dom" for free and get a clearer separation by not manipulating the html node itself.

A minor downside is that you add a extra html node, and therefore it may affect styling (eg: direct > selectors).

General use

Simply wrap your html node with

<modular-behaviour name="myGlobalFunction" data-someconfig="test">
  <div class="somenode">Some content here</div>
</modular-behaviour>

If the function myGlobalFunction is defined in the global scope, it will be called with two parameters:

  • the element (by default, the first child element of the custom element)
  • a config array (the data attributes on the custom elements)

WARNING : module names are CaSe SenSitiVe! For instance, jquery datable needs to use DataTable or FilePond needs a uppercase P.

What if my arguments are different ?

Indeed, this library expect the very classic (el, opts) convention. If you have a function using other arguments (eg: selector, opt1, opt2) you need to wrap that function into another function that accepts (el, opts) instead.

Namespaced plugins

Avoid polluting the global namespace by defining a App namespace.

<modular-behaviour name="App.myFunction" data-someconfig="test">
  <div class="somenode">Some content here</div>
</modular-behaviour>

Load order

One of the nice thing with this library is that load order doesn't matter. You can define your html first, then load the js. Or the opposite.

Js first

The javascript function is already available in the global scope and can be called. This will not be the case if you defer the loading of your scripts or use js modules.

The js function is immediately triggered when the custom element is ready.

Js second

If you deferred (or async) the loading of the js code, the html node may be defined before the js function responsible for initializing the html node is available.

As a first measure, we try to initialize everything on domReady.

After that, the callback is added to a watch list. We poll the scope (fast first, then slower) until the callback function is available. If polling doesn't result in any result after a certain time, it stops.

Provider scripts

Polling the scope is not ideal. It's much better to know WHEN the js callback is made available and run only when ready.

You can add a provides attribute to a script. Onload, it will trigger all matching nodes.

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js" type="module" provides="jQuery.select2"></script>

Init / pending classes

While loading, the nodes get a modular-behaviour-pending class. Once initialized, they get a modular-behaviour-initialized class. This can help dealing with loading/init states.

modular-behaviour {
  visibility: hidden;
}
modular-behaviour.modular-behaviour-initialized {
  visibility: visible;
}

Advanced configuration

Using data attributes

The default way to deal with configuration is through data attributes in the modular-behaviour element. They will be passed on as an array to the constructor function.

Using json config

You can use a special data-mb-config attribute.

You can also add a custom config a json in a template with the class modular-behaviour-config. It is recommended to put the template at the end of the element.

<modular-behaviour name="Cleave">
  <input class="input-date-a" placeholder="YYYY-MM-DD" type="tel" />
  <template class="modular-behaviour-config">{"myconfig": "test"}</template>
</modular-behaviour>

Passing functions

JSON is great, but what when you want to pass callbacks, eg: onRender, onChange etc...

In order to support this, you can pass dedicated function object values that have two signatures These will be replaced by the replaceCallbacks utility.

Simple strings (evaluated again window object):

"onChange": {"__fn": "myGlobalCallback"}
"onChange": {"__fn": "app.myGlobalCallback"} // can be nested

Full definitions using array [args, body] that use new Function constructor:

"onChange": {"__fn":["ev,inst","console.log(ev)"]}}

Using custom script

Json is nice, but it's limited in the way it can express callbacks for example. A more powerful alternative is to load the configuration from a variable or a function.

When doing so, instead of being loaded as json, the config will be injected as a js script.

The script is executed after the function is available, but before its called.

<modular-behaviour name="Cleave" config="myconfig">
  <input class="input-date-a" placeholder="YYYY-MM-DD" type="tel" />
  <template class="modular-behaviour-config">var myconfig = {myconfig: "test"} ; // my config</template>
</modular-behaviour>

Manual lookup

If you don't want to use the automatic system that look for a function, you can initialize things yourself.

<modular-behaviour name="yetToDefine" manual>
  <div>init</div>
</modular-behaviour>

Then call

window["yetToDefine"] = function (el, opts) {};
customElements.get("modular-behaviour").run("yetToDefine");

NOTE: manual lookup still initialize everything automatically if yetToDefine is available when the custom element is loaded. It only prevents the polling of the scope to let you determine when the dependencies for this element are indeed loaded.

Lazy init

But what if you html nodes are not visible, because you have a long page or tabs ? Wouldn't it be a waste to initialize them immediately ?

Indeed! This is why you can set a lazy attribute.

<modular-behaviour name="imLazy" lazy>
  <div></div>
</modular-behaviour>

In this example, imLazy will only be looked for when the node is actually visible.

NOTE: lazy elements don't get a pending class (that is specific for elements waiting to be initialized due to a missing callback in the global scope).

After init, a connected event is fired if you want to do more things.

Self contained elements

You can go one step further than lazy loading, you can actually let modular behaviour load your js modules. It only requires that you set a src attribute that points to a js file that export default the class you want to load.

Here is an example using my bootstrap5-tags library.

<modular-behaviour name="Tags" src="https://cdn.jsdelivr.net/npm/[email protected]/tags.min.js" lazy>
  <select class="form-select" id="tags" name="tags[]" multiple data-allow-clear="1" required>
    <option selected="selected" disabled hidden value="">Choose a tag...</option>
    <option value="1" selected="selected">Apple</option>
    <option value="2">Banana</option>
    <option value="3">Orange</option>
  </select>
</modular-behaviour>

Cleaning up

If an element is removed from the dom, it can be important to clean up after yourself to avoid memory issues or cluttering the dom

By default, modular behaviour will call destroy on the created object. Otherwise, you may need to clean up yourself by listening to the disconnected event.

Supported attributes

| Name | Default | Description | | -------- | ------- | ------------------------------------------------------------------------------------------------------- | | name | null | The name of the function to call in the global scope. It can be nested (eg: App.Func) | | config | null | The name of the var or function that provides the configuration | | manual | false | Don't use auto init system | | lazy | false | Lazily init html nodes when visible in viewport | | src | '' | Path to a js file that export a default class that will be imported dynamically and used as constructor | | selector | '' | Custom selector to select the target node (first child element by default) | | func | '' | Alternative function to call instead of the one provided by name |

Demo

Please check demo.html for some sample usages

Improved integration

As part of my effort to have consistent libraries, I've created Formidable Elements which cover many edge cases as well, like cleaning up when an element is destroyed.

What about X feature from V2 ?

Some features are not available anymore: transformers, hooks, multiple callbacks... These are mostly unecessary and can be replaced by custom constructors.