@evolv/mutate
Version:
A library of standard DOM mutations by Evolv AI.
149 lines (107 loc) • 4.35 kB
Markdown
Mutate is a library of helpers to maintain persistent
modifications to the rendered DOM of a web page for
implementation of temporary, experimental changes and
features for rapid experimentation.
It is intended to be framework independent and equally
effective for SPAs and traditionally rendered webpages.
```shell
$ npm install @evolv/mutate
```
Clone the repository and run the following commands.
```shell
$ npm install
$ npm run build
```
There is a demo site included.
*Content warning: flashing lights*
```shell
$ npm run demo
```
The API for Mutate is similar in a lot of ways to the API of jQuery
with a significant difference, that is selectors (`Collectors`) refer
to all current and future matching `Element`s, and the functions to modify
the DOM (`Mutations`) are persistent.
This means that you don't need to worry about timing, or dynamic areas
of the rendered page.
Mutate also provides the ability to "project" `Element`s of the DOM into
other `Element`s of the DOM, binding all interactions with the "projection"
back to the original `Element`s.
As everyone building variants have learned the hard way, most `Element`s
are dependent on their location in the DOM for both style and functionality.
Projection allows the implementer to "move" and restyle `Element`s without
losing the position dependent functionality of the original `Element`s.
### Importing
```javascript
import {collect, mutate} from '@evolv/mutate';
```
The basic flow when using Mutate is to first define a Collector.
```javascript
collect('<selector>', '<collector name>');
```
Then to define a Mutator for the Collector.
```javascript
mutate('<collector name>').hide();
```
Mutators allow for Mutations to be chained together, similar to jQuery which
will be evaluated in order of invocation.
```javascript
mutate('<collector name>').text('<new text value>').classes({'<new class>': true});
```
Mutate can detect runaway collectors (e.g. when a mutation keeps creating
elements that re-trigger its own collector) and automatically pause the affected
mutators.
Enable and configure the loop guard at bootstrap time:
```javascript
import { bootstrap } from '@evolv/mutate';
bootstrap({
loopGuard: {
enabled: true,
windowMs: 1000, // size of the observation window
threshold: 10, // max collector events allowed within the window
cooldownMs: 5000, // time to wait before the same collector can trigger again
onDetect: (event) => {
console.warn('Loop detected', event);
},
},
});
```
You can also toggle or reconfigure it at runtime through `collect.loopGuard`,
either via the module export or the global `evolv.collect.loopGuard` once
bootstrapped:
```javascript
import { collect } from '@evolv/mutate';
collect.loopGuard.enable({ threshold: 3, windowMs: 500 });
// To disable again:
evolv.collect.loopGuard.disable();
```
Every detection dispatches a `evolv:mutate-loop-detected` `CustomEvent` so host
applications can listen globally:
```javascript
document.addEventListener('evolv:mutate-loop-detected', (event) => {
console.log('Loop guard tripped', event.detail);
});
```
When the guard trips, the relevant collector is paused, and the active mutators
are paused & reverted so the page remains responsive.
> **Notes**
> - The guard is disabled by default; enable it explicitly and tune `threshold`, `windowMs`, and `cooldownMs` (default `5000ms`) for your scenario. Starting with a higher threshold (default `10` events within `1000ms`) avoids flagging legitimate quick mutations such as `text()` + `styles()`.
> - Built-in effects automatically revert, but custom mutations must provide a `revert` handler if you expect the guard to undo their DOM changes.
#### How to test your changes
1. Run npm start
2. Create a simple website or use codesandbox
3. Add a snippet to head `<script src="http://localhost:8080/index.js"></script>` (make sure that your local is running on 8080, otherwise update src)
4. Apply changes to the website in the console
```javascript
evolv.collect('h1', 'heading')
evolv.mutate('heading').html('Test');
```
[](https://evolv-ai.github.io/mutate)