modular-behaviour.js

3.2.1 • Public • Published

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/select2@4.1.0-rc.0/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/bootstrap5-tags@1.4.35/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.

Package Sidebar

Install

npm i modular-behaviour.js

Weekly Downloads

18

Version

3.2.1

License

MIT

Unpacked Size

76.6 kB

Total Files

18

Last publish

Collaborators

  • lekoala