sashafirsov / css-chain

ApiChain and CssChain JS. Inherits collection API from element
Apache License 2.0
4 stars 0 forks source link

ApiChain and CssChain JS.

HTML template/slot and DOM manipulation library

Collection API inherits the element API and Array.

git GitHub | Demo: css-chain | tests project

NPM version coverage

CssChain.js

html elements methods

CssChain searches HTML by css and returns an Array inherited object which has all methods and properties of its elements. When method is called, each element would invoke this method and then same CssChain object is returned.

import $ from 'https://unpkg.com/css-chain@1/CssChain.js'
$( '*[title]', rootEL ).addEventListener( 'click', ev=> alert(ev.target.title) );

^^ adds event listener to all selected elements in rootEl DOM tree

chained calls HTMLElement API
chained call chained call
    CssChain( 'a' )
        .addEventListener( 'mouseover' , ev=> alert(ev.target.classList.add('hovered') ) )
        .addEventListener( 'mouseleave', ev=> alert(ev.target.classList.remove('hovered') ) )
        .on( 'focus'     , ev=> alert(ev.target.classList.add('focused') ) )
        .on( 'mouseleave', ev=> alert(ev.target.classList.remove('focused') ) )

^^ adds multiple event handlers in chainable dot notation.

Typescript

import CssChain from 'css-chain' code has typings enabled by default. The chain type is a mixin of HTMLElementMixin and array of this mixin. To add the type checking for custom element(s) CssChain accept generics parameter and needs at least once initialisation by 3rd parameter as array of either custom element tags or classes.

import {CssChain as $ } from 'css-chain';

class DemoElement {  b:string; setB(_b: string) { this.b = _b; } }
customElements.define('demo-element', DemoElement );

const $X = $<DemoElement>('demo-element', el, ['demo-element']);

expect($X.b).to.equal('initial');
$X.setB('1');

CssChain initialization

To avoid passing the prototype each time, it could be initialized in module global scope:

import {CssChain as $ } from 'css-chain';
// import or declare DemoElement
$([],[],[DemoElement]);
// now all methods and members of DemoElement available in CssChain everywhere.

special methods

Light DOM

Shadow DOM and its twin Light DOM allows to use template and work with dynamic content withing template. Both often represent the content of web component as convenient way to generate DOM subtree by JavaScript.

In the light DOM as opposite to Shadow DOM the render root is part of usual DOM without engaging the shadowRoot. There the template clone would take a render root DOM subtree payload as the source for its slot assignment. Once slots are populated into this clone, it would replace the render root content.

Of course such benefits of shadow DOM as template reuse and CSS encapsulation are lost in light DOM. But the modular development with templates, HTML5 standard dynamic content use convention and API are still around.

global CSS

is available for styling the light DOM components, which could be advantage in some environments where css encapsulation of shadow DOM in web components prevents usual css styling.

Light DOM API

slots

<slot name='xxx'></slot> is an HTML tag, a marker where dynamic content would be placed. It works with shadow and light DOM in same manner.

When used with template() or by shadow DOM, dynamic content is taken from DOM subtree marked by slot='xxx' attribute.

Otherwise slot content could be manipulated by JS API:

html elements properties

When property is assigned to collection, this property would be set for all elements in collection. The property get would return property from 1st element.

    import { CssChain as $ } from '../src/CssChain.js';
    $( 'input' ).value = 'not defined'; // all INPUT elements would have new value set
    v = $( 'input' ).prop( value,'not defined' ); // same as ^^
    let  v = $( 'input' ).value; // variable would receive the 1st INPUT element value
    v = $( 'input' ).prop( value ); // same as ^^

ApiChain.js

Array of raw objects

    $([ {a:1},{a:2} ]).a=1;    // all arr elements property `a` set to 1
    v = $([ {a:1},{a:2} ]).a;  // 1st element property `a` is returned, i.e. 1
    $( [ { a:1,f(v){ this.a=v} }, { b:2,f(v){ this.b=v}} ])
        .f(3); // method called on each element, result [{a:3},{b:3}]

Array of class objects

Could be initiated in same fashion as raw objects. But for performance better to provide the reference object as a second parameter:

    class A{ f(){} }
    const x = new A(), y = new A();
    $( [x,y], A ).f()

cssChain - initiated by css selector from HTMLElement API

Registered interfaces are taken from window object by window.HTML*Element pattern. To use API from custom elements, add those in array of last

Api selected either from elements in collection or from window.HTML*Element.

import {CssChain as $} from 'api-chain/CssChain.js';
$('input').value=''; // set all INPUT elements on page value to blank string
$('input').forEach( (el,i) => el.value=i ) // all array methods available
$('[type="checkbox"]')
  .prop( 'checked', false ) // set property and return same CssChain 
  .addEventListener('click' ,function(){  }) // methods are called for each element
  .addEventListener('change',function(){ this.checked; }) // and could be chained
  .on( 'hover', ()=>showTooltip() ) // 'on' is alias to `addEventListener`

const firstFormUrl = $('form').action; // returns property valuye of 1st element in collection.
const firstLinkUrl = $('a').href; // returns property valuye of 1st element in collection.
$('a').prop('href'); // same as above
$('a').attr('href'); // same as getAttribute() for 1st element in collection

optimization

API simulation is expensive when done on each occasion. Pre-generated API wrappers could be added on app load time. CssChain on module load applied the whole set of window.HTML*Element.

In the call of ApiChain the last parameter is an array of prototype objects. The classes listed in this array would be stored in global map and generated API would be reused next time same class is listed within last parameter.

class A{ f1(){} } class B{ f2(){} }
const a = new A, b = new A;
ApiChain( [a,b] ).f1().f2() // would generate API on each call
ApiChain( [], [A,B] ) // would generate from prototypes array on 1st call
ApiChain( [a,b] ).f1().f2() // would reuse API generated in previous call

Samples of use

PokéAPI Explorer: PokéAPI Explorer