UNPKG

spect

Version:
256 lines (197 loc) 7.5 kB
<!-- <p align="center">▶ <a href="https://codepen.io/dyv/pen/oNXXZEb" target="_blank"><strong>Run</strong></a></p> <br/> --> # <sub><img alt="subscript" src="./logo2.svg" height=30 /></sub> spect   <a href="https://github.com/spectjs/spect/actions/workflows/test.yml"><img src="https://github.com/spectjs/spect/actions/workflows/test.yml/badge.svg"/></a> <a href="https://bundlephobia.com/result?p=spect"><img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/spect?label=size"></a> <a href="https://npmjs.org/package/spect"><img alt="npm" src="https://img.shields.io/npm/v/spect"></a> > Observe selectors in DOM. #### _`spect( container=document, selector, handler? )`_ Observes _`selector`_ in _`container`_, invokes `handler` any time matching elements appear or disappear.<br/> Handler can return a teardown function, called for unmatched elements.<br/> Returns live collection of elements. ```js import spect from 'spect'; // assign aspect const foos = spect('.foo', el => { console.log('connected'); return () => console.log('disconnected'); }); // modify DOM const foo = document.createElement('div'); foo.className = 'foo'; document.body.append(foo); // ... "connected" foo.remove(); // ... "disconnected" ``` #### _`spect(element[s], handler)`_ Listens for connected/disconnected events for the list of elements. (alternative to [fast-on-load](https://www.npmjs.com/package/fast-on-load)) ```js let nodes = [...document.querySelectorAll('.foo'), document.createElement('div')]; // assign listener spect(nodes, el => { console.log("connected"); return () => console.log("disconnected"); }); // modify DOM document.body.appendChild(nodes.at(-1)) // ... "connected" nodes.at(-1).remove() // ... "disconnected" ``` #### _<code>spect\`selector\`</code>_ Creates live collection of elements matching the selector. Collection extends Array and implements Set / HTMLColection interfaces. ```js const foos = spect`.foo`; // live collection foos[idx], foos.at(idx) // Array foos.has(el), foos.add(el), foos.delete(el) // Set foos.item(idx), foos.namedItem(elementId) // HTMLCollection foos.dispose() // destroy selector observer / unsubscribe ``` ### Technique It combines selector parts indexing from [selector-observer](https://github.com/josh/selector-observer) for simple queries and animation events from [insertionQuery](https://github.com/naugtur/insertionQuery) for complex selectors. Simple selector is id/name/class/tag followed by classes or attrs. * `#a`, `.x.y`, `[name="e"].x`, `*`, `a-b-c:x` - simple selectors. * `a b`, `#b .c` - complex selectors. <!-- ## Examples <details><summary>Hello World</summary> ```html <div class="user">{{ user.name || "Loading..." }}</div> <script type="module"> import spect from 'spect' import templize from 'templize' // initialize template spect('.user', async el => templize(el, { user: (await fetch('/user')).json() // value is available when resolved })) </script> ``` </details> <details><summary>Timer</summary> ```html <time class="timer">{{ count }}</time> <time class="timer">{{ count }}</time> <script type="module"> import spect from 'spect' import templize from 'templize' spect('.timer', timer => { const params = templize(timer, { count: 0 }) const id = setInterval(() => params.count++, 1000) return () => clearInterval(id) }) </script> ``` </details> <details><summary>Counter</summary> ```html <output id="count">{{ count }}</output> <button id="inc" onclick="{{ inc }}">+</button> <button id="dec" onclick="{{ dec }}">-</button> <script type="module"> import spect from 'spect' import v from 'value-ref' import templize from 'templize' const count = v(0) spect('#count', el => templize(el, { count })) // bind events via HTML template spect('#inc', el => templize(el, { inc: () => count.value++ })) spect('#dec', el => templize(el, { dec: () => count.value-- })) </script> ``` </details> <details><summary>Todo list</summary> ```html <form class="todo-form"> <label for="add-todo"> <span>Add Todo</span> <input name="text" required> </label> <button type="submit">Add</button> <ul class="todo-list">{{ todos }}<ul> </form> <script type="module"> import spect from 'spect' import v from 'value-ref' import h from 'hyperf' import tpl from 'templize' const todos = v([]) spect('.todo-list', el => tpl(el, { todos: v.from(todos, item => h`<li>${ item.text }</li>`) })) spect('.todo-form', form => form.addEventListener('submit', e => { e.preventDefault() if (!form.checkValidity()) return todos.value = [...todos.value, { text: form.text.value }] form.reset() })) </script> ``` </details> <details><summary>Form validator</summary> ```html <form id="email-form"> <label for="email">Please enter an email address:</label> <input id="email" onchange={{ validate }}/> The address is {{ valid ? "valid" : "invalid" }} </form> <script type="module"> import spect from 'spect' import templize from 'templize' const isValidEmail = s => /.+@.+\..+/i.test(s) spect('#email-form', form => { const params = templize(form, { valid: false, validate: () => params.valid = isValidEmail(e.target.value) }) }) </script> ``` </details> <details><summary>Prompt</summary> ```html <dialog class="dialog" open={{showPrompt}}> Proceed? <menu> <button onclick={{cancel}}>Cancel</button> <button onclick={{confirm}}>Confirm</button> </menu> </dialog> <script> import v from 'value-ref' import spect from 'spect' spect('.dialog', el => { const showPrompt = v(false), proceed = v(false) templize(el, { showPrompt, proceed, cancel() {showPrompt.value = proceed.value = false;}, confirm() {showPrompt.value = false; proceed.value = true;} }) }) </script> ``` </details> [See all examples](examples). --> <!-- ## Best Buddies * [value-ref](https://github.com/spectjs/value-ref) − value container with observable interface. Indispensible for reactive data. * [templize](https://github.com/spectjs/templize) − DOM buddy - hooks up reactive values to template parts. * [hyperf](https://github.com/spectjs/hyperf) − builds HTML fragments with reactive fields. * [subscribable-things](https://github.com/chrisguttandin/subscribable-things) − collection of observables for different browser APIs. --> <!-- * [element-props](https://github.com/spectjs/element-props) − unified access to element props with observable support. Comes handy for organizing components. --> <!-- * [strui](https://github.com/spectjs/strui) − collection of UI streams, such as router, storage etc. Comes handy for building complex reactive web-apps (spect, rxjs etc). --> ## Alternatives [insertionQuery](https://github.com/naugtur/insertionQuery), [selector-observer](https://github.com/josh/selector-observer), [qso](https://www.npmjs.com/package/qso), [qsa-observer](https://www.npmjs.com/package/qsa-observer), [element-observer](https://github.com/WebReflection/element-observer), [livequery](https://github.com/hazzik/livequery), [selector-listener](https://github.com/csuwildcat/SelectorListener), [mutation-summary](https://github.com/rafaelw/mutation-summary), [fast-on-load](https://ghub.io/fast-on-load), [selector-set](https://github.com/josh/selector-set), [rkusa/selector-observer](https://github.com/rkusa/selector-observer). <p align="center">ॐ</p>