dom-mutator
Advanced tools
Comparing version 0.3.1 to 0.3.2
@@ -283,3 +283,3 @@ 'use strict'; | ||
var newElements = new Set(); | ||
var nodes = document.body.querySelectorAll(mutation.selector); | ||
var nodes = document.querySelectorAll(mutation.selector); | ||
nodes.forEach(function (el) { | ||
@@ -328,3 +328,3 @@ newElements.add(el); | ||
refreshAllElementSets(); | ||
observer.observe(document.body, { | ||
observer.observe(document.documentElement, { | ||
childList: true, | ||
@@ -331,0 +331,0 @@ subtree: true, |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=/^[a-zA-Z:_][a-zA-Z0-9:_.-]*$/,e={revert:function(){}},n=new Map,r=new Set;function u(t){var e=n.get(t);return e||n.set(t,e={el:t,attributes:{}}),e}function i(t,e,n,r,u){var i=n(t),a={isDirty:!1,originalValue:i,virtualValue:i,mutations:[],el:t,observer:new MutationObserver((function(){var e=n(t);e!==a.virtualValue&&(a.originalValue=e,u(a))})),runMutations:u,setValue:r,getCurrentValue:n};return a.observer.observe(t,function(t){return"html"===t?{childList:!0,subtree:!0,attributes:!0,characterData:!0}:{childList:!1,subtree:!1,attributes:!0,attributeFilter:[t]}}(e)),a}function a(t){var e=t.originalValue;return t.mutations.forEach((function(t){return e=t.mutate(e)})),e}function s(t,e){var n=e.getCurrentValue(e.el);e.virtualValue=t,t!==n&&(e.isDirty=!0,V||(V=!0,requestAnimationFrame(k)))}function o(t){s(function(t){return v||(v=document.createElement("div")),v.innerHTML=t,v.innerHTML}(a(t)),t)}function l(t){var e=function(t,e){return e.mutations.forEach((function(e){return e.mutate(t)})),t}(new Set(t.originalValue.split(/\s+/).filter(Boolean)),t);s(Array.from(e).filter(Boolean).join(" "),t)}function c(t){s(a(t),t)}var f=function(t){return t.innerHTML},m=function(t,e){return t.innerHTML=e};function d(t){var e=u(t);return e.html||(e.html=i(t,"html",f,m,o)),e.html}var v,b=function(t,e){return e?t.className=e:t.removeAttribute("class")},h=function(t){return t.className};function p(t){var e=u(t);return e.classes||(e.classes=i(t,"class",h,b,l)),e.classes}function M(t,e){var n=u(t);return n.attributes[e]||(n.attributes[e]=i(t,e,(function(t){return t.getAttribute(e)||""}),(function(t,n){return n?t.setAttribute(e,n):t.removeAttribute(e)}),c)),n.attributes[e]}function y(t,e,r){if(r.isDirty){r.isDirty=!1;var u=r.virtualValue;r.mutations.length||function(t,e){var r,u,i=n.get(t);if(i)if("html"===e)null==(r=i.html)||null==(u=r.observer)||u.disconnect(),delete i.html;else if("class"===e){var a,s;null==(a=i.classes)||null==(s=a.observer)||s.disconnect(),delete i.classes}else{var o,l,c;null==(o=i.attributes)||null==(l=o[e])||null==(c=l.observer)||c.disconnect(),delete i.attributes[e]}}(t,e),r.setValue(t,u)}}var w,V=!1;function g(t,e){t.html&&y(e,"html",t.html),t.classes&&y(e,"class",t.classes),Object.keys(t.attributes).forEach((function(n){y(e,n,t.attributes[n])}))}function k(){V=!1,n.forEach(g)}function A(t,e){if(t.elements.delete(e),"html"===t.kind){var n=d(e),r=n.mutations.indexOf(t);-1!==r&&n.mutations.splice(r,1),n.runMutations(n)}else if("class"===t.kind){var u=p(e),i=u.mutations.indexOf(t);-1!==i&&u.mutations.splice(i,1),u.runMutations(u)}else if("attribute"===t.kind){var a=M(e,t.attribute),s=a.mutations.indexOf(t);-1!==s&&a.mutations.splice(s,1),a.runMutations(a)}}function E(t){var e=new Set(t.elements),n=new Set;document.body.querySelectorAll(t.selector).forEach((function(r){n.add(r),e.has(r)||function(t,e){if(t.elements.add(e),"html"===t.kind){var n=d(e);n.mutations.push(t),n.runMutations(n)}else if("class"===t.kind){var r=p(e);r.mutations.push(t),r.runMutations(r)}else if("attribute"===t.kind){var u=M(e,t.attribute);u.mutations.push(t),u.runMutations(u)}}(t,r)})),e.forEach((function(e){n.has(e)||A(t,e)}))}function S(){r.forEach(E)}function O(){"undefined"!=typeof document&&(w||(w=new MutationObserver((function(){S()}))),S(),w.observe(document.body,{childList:!0,subtree:!0,attributes:!1,characterData:!1}))}function x(t){return"undefined"==typeof document?e:(r.add(t),E(t),{revert:function(){var e;e=t,new Set(e.elements).forEach((function(t){A(e,t)})),e.elements.clear(),r.delete(e)}})}function L(t,e){return x({kind:"html",elements:new Set,mutate:e,selector:t})}function D(t,e){return x({kind:"class",elements:new Set,mutate:e,selector:t})}function j(n,r,u){return t.test(r)?x("class"===r||"className"===r?{kind:"class",elements:new Set,mutate:function(t){var e=u(Array.from(t).join(" "));t.clear(),e.split(/\s+/g).filter(Boolean).forEach((function(e){t.add(e)}))},selector:n}:{kind:"attribute",attribute:r,elements:new Set,mutate:u,selector:n}):e}O();var H={html:L,classes:D,attribute:j,declarative:function(t){var n=t.selector,r=t.action,u=t.value,i=t.attribute;if("html"===i){if("append"===r)return L(n,(function(t){return t+u}));if("set"===r)return L(n,(function(){return u}))}else if("class"===i){if("append"===r)return D(n,(function(t){return t.add(u)}));if("remove"===r)return D(n,(function(t){return t.delete(u)}));if("set"===r)return D(n,(function(t){t.clear(),t.add(u)}))}else{if("append"===r)return j(n,i,(function(t){return t+u}));if("set"===r)return j(n,i,(function(){return u}))}return e}};exports.connectGlobalObserver=O,exports.default=H,exports.disconnectGlobalObserver=function(){w&&w.disconnect()}; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=/^[a-zA-Z:_][a-zA-Z0-9:_.-]*$/,e={revert:function(){}},n=new Map,r=new Set;function u(t){var e=n.get(t);return e||n.set(t,e={el:t,attributes:{}}),e}function i(t,e,n,r,u){var i=n(t),a={isDirty:!1,originalValue:i,virtualValue:i,mutations:[],el:t,observer:new MutationObserver((function(){var e=n(t);e!==a.virtualValue&&(a.originalValue=e,u(a))})),runMutations:u,setValue:r,getCurrentValue:n};return a.observer.observe(t,function(t){return"html"===t?{childList:!0,subtree:!0,attributes:!0,characterData:!0}:{childList:!1,subtree:!1,attributes:!0,attributeFilter:[t]}}(e)),a}function a(t){var e=t.originalValue;return t.mutations.forEach((function(t){return e=t.mutate(e)})),e}function s(t,e){var n=e.getCurrentValue(e.el);e.virtualValue=t,t!==n&&(e.isDirty=!0,g||(g=!0,requestAnimationFrame(y)))}function o(t){s(function(t){return v||(v=document.createElement("div")),v.innerHTML=t,v.innerHTML}(a(t)),t)}function l(t){var e=function(t,e){return e.mutations.forEach((function(e){return e.mutate(t)})),t}(new Set(t.originalValue.split(/\s+/).filter(Boolean)),t);s(Array.from(e).filter(Boolean).join(" "),t)}function c(t){s(a(t),t)}var f=function(t){return t.innerHTML},m=function(t,e){return t.innerHTML=e};function d(t){var e=u(t);return e.html||(e.html=i(t,"html",f,m,o)),e.html}var v,b=function(t,e){return e?t.className=e:t.removeAttribute("class")},h=function(t){return t.className};function p(t){var e=u(t);return e.classes||(e.classes=i(t,"class",h,b,l)),e.classes}function M(t,e){var n=u(t);return n.attributes[e]||(n.attributes[e]=i(t,e,(function(t){return t.getAttribute(e)||""}),(function(t,n){return n?t.setAttribute(e,n):t.removeAttribute(e)}),c)),n.attributes[e]}function w(t,e,r){if(r.isDirty){r.isDirty=!1;var u=r.virtualValue;r.mutations.length||function(t,e){var r,u,i=n.get(t);if(i)if("html"===e)null==(r=i.html)||null==(u=r.observer)||u.disconnect(),delete i.html;else if("class"===e){var a,s;null==(a=i.classes)||null==(s=a.observer)||s.disconnect(),delete i.classes}else{var o,l,c;null==(o=i.attributes)||null==(l=o[e])||null==(c=l.observer)||c.disconnect(),delete i.attributes[e]}}(t,e),r.setValue(t,u)}}var V,g=!1;function k(t,e){t.html&&w(e,"html",t.html),t.classes&&w(e,"class",t.classes),Object.keys(t.attributes).forEach((function(n){w(e,n,t.attributes[n])}))}function y(){g=!1,n.forEach(k)}function E(t,e){if(t.elements.delete(e),"html"===t.kind){var n=d(e),r=n.mutations.indexOf(t);-1!==r&&n.mutations.splice(r,1),n.runMutations(n)}else if("class"===t.kind){var u=p(e),i=u.mutations.indexOf(t);-1!==i&&u.mutations.splice(i,1),u.runMutations(u)}else if("attribute"===t.kind){var a=M(e,t.attribute),s=a.mutations.indexOf(t);-1!==s&&a.mutations.splice(s,1),a.runMutations(a)}}function A(t){var e=new Set(t.elements),n=new Set;document.querySelectorAll(t.selector).forEach((function(r){n.add(r),e.has(r)||function(t,e){if(t.elements.add(e),"html"===t.kind){var n=d(e);n.mutations.push(t),n.runMutations(n)}else if("class"===t.kind){var r=p(e);r.mutations.push(t),r.runMutations(r)}else if("attribute"===t.kind){var u=M(e,t.attribute);u.mutations.push(t),u.runMutations(u)}}(t,r)})),e.forEach((function(e){n.has(e)||E(t,e)}))}function S(){r.forEach(A)}function O(){"undefined"!=typeof document&&(V||(V=new MutationObserver((function(){S()}))),S(),V.observe(document.documentElement,{childList:!0,subtree:!0,attributes:!1,characterData:!1}))}function x(t){return"undefined"==typeof document?e:(r.add(t),A(t),{revert:function(){var e;e=t,new Set(e.elements).forEach((function(t){E(e,t)})),e.elements.clear(),r.delete(e)}})}function L(t,e){return x({kind:"html",elements:new Set,mutate:e,selector:t})}function D(t,e){return x({kind:"class",elements:new Set,mutate:e,selector:t})}function j(n,r,u){return t.test(r)?x("class"===r||"className"===r?{kind:"class",elements:new Set,mutate:function(t){var e=u(Array.from(t).join(" "));t.clear(),e.split(/\s+/g).filter(Boolean).forEach((function(e){t.add(e)}))},selector:n}:{kind:"attribute",attribute:r,elements:new Set,mutate:u,selector:n}):e}O();var H={html:L,classes:D,attribute:j,declarative:function(t){var n=t.selector,r=t.action,u=t.value,i=t.attribute;if("html"===i){if("append"===r)return L(n,(function(t){return t+u}));if("set"===r)return L(n,(function(){return u}))}else if("class"===i){if("append"===r)return D(n,(function(t){return t.add(u)}));if("remove"===r)return D(n,(function(t){return t.delete(u)}));if("set"===r)return D(n,(function(t){t.clear(),t.add(u)}))}else{if("append"===r)return j(n,i,(function(t){return t+u}));if("set"===r)return j(n,i,(function(){return u}))}return e}};exports.connectGlobalObserver=O,exports.default=H,exports.disconnectGlobalObserver=function(){V&&V.disconnect()}; | ||
//# sourceMappingURL=dom-mutator.cjs.production.min.js.map |
@@ -279,3 +279,3 @@ var validAttributeName = /^[a-zA-Z:_][a-zA-Z0-9:_.-]*$/; | ||
var newElements = new Set(); | ||
var nodes = document.body.querySelectorAll(mutation.selector); | ||
var nodes = document.querySelectorAll(mutation.selector); | ||
nodes.forEach(function (el) { | ||
@@ -324,3 +324,3 @@ newElements.add(el); | ||
refreshAllElementSets(); | ||
observer.observe(document.body, { | ||
observer.observe(document.documentElement, { | ||
childList: true, | ||
@@ -327,0 +327,0 @@ subtree: true, |
{ | ||
"version": "0.3.1", | ||
"version": "0.3.2", | ||
"license": "MIT", | ||
@@ -4,0 +4,0 @@ "main": "dist/index.js", |
140
README.md
# DOM Mutator | ||
Apply persistent DOM mutations on top of anything (static HTML, React, Vue, etc.) | ||
For those times you need to apply persistent DOM changes on top of HTML you don’t control. | ||
View demo: https://growthbook.github.io/dom-mutator/ | ||
```ts | ||
const mutation = mutate.html("#greeting", html => html + ' world'); | ||
// works even if the selector doesn't exist yet | ||
document.body.innerHTML += "<div id='greeting'>hello</div>"; | ||
// "hello world" | ||
// re-applies if there's an external change | ||
document.getElementById('greeting').innerHTML = 'hola'; | ||
// "hola world" | ||
// Revert to the last externally set value | ||
mutation.revert(); | ||
// "hola" | ||
``` | ||
```ts | ||
import mutate from "dom-mutator"; | ||
@@ -12,3 +33,3 @@ | ||
mutate.attr(".get-started", "title", (oldVal) => "This is my new title attribute"); | ||
mutate.attribute(".get-started", "title", (oldVal) => "This is my new title attribute"); | ||
``` | ||
@@ -20,75 +41,116 @@ | ||
* Super fast and light-weight (1Kb gzipped) | ||
* Mutations will apply to elements that match the selector (even ones that don't exist yet) | ||
* Mutations persist even if the underlying element is updated externally (e.g. by a React render) | ||
* Persists mutations even if the underlying element is updated externally (e.g. by a React render) | ||
* Picks up new matching elements that are added to the DOM | ||
* Easily remove a mutation at any time | ||
`yarn add dom-mutator` OR `npm install --save dom-mutator`. | ||
![Build Status](https://github.com/growthbook/dom-mutator/workflows/CI/badge.svg) | ||
## Basic Usage | ||
## Installation | ||
innerHTML example: | ||
Install with npm or yarn (recommended): | ||
```ts | ||
`yarn add dom-mutator` OR `npm install --save dom-mutator`. | ||
```js | ||
import mutate from "dom-mutator"; | ||
... | ||
``` | ||
// Mutate the innerHTML of an element | ||
const stop = mutate.html("#greeting", html => html + ' world'); | ||
OR use with unpkg: | ||
// works even if the selector doesn't exist yet | ||
document.body.innerHTML += "<div id='greeting'>hello</div>"; | ||
```html | ||
<script type="module"> | ||
import mutate from "https://unpkg.com/dom-mutator/dist/dom-mutator.esm.js"; | ||
... | ||
</script> | ||
``` | ||
//**** div innerHTML = "hello world" at this point! | ||
## Usage | ||
// mutation persists even if there is an external change | ||
document.getElementById('greeting').innerHTML = 'hola'; | ||
There are 4 mutate methods available: `html`, `classes`, `attribute`, and `declarative`. | ||
//**** div innerHTML = "hola world" | ||
### html | ||
// Stop mutating the element | ||
stop(); | ||
Mutate an element's innerHTML | ||
//**** div innerHTML = "hola" (the last externally set value) | ||
```ts | ||
// Signature | ||
mutate.html(selector: string, (oldInnerHTML: string) => string); | ||
// Example | ||
mutate.html("h1", x => x.toUpperCase()); | ||
``` | ||
## Available Mutation Types | ||
### classes | ||
The `mutate` object has a few different methods you can call: | ||
Mutate the set of classes for an element | ||
- html | ||
- classList | ||
- attr | ||
```ts | ||
// Signature | ||
mutate.classes(selector: string, (classes: Set<string>) => void); | ||
// Example | ||
mutate.classes("h1", (classes) => { | ||
classes.add("green"); | ||
classes.remove("red"); | ||
}); | ||
``` | ||
### attribute | ||
Mutate the value of an HTML element's attribute | ||
```ts | ||
mutate.html("h1", html => html.toUpperCase()); | ||
// Signature | ||
mutate.attribute(selector: string, attribute: string, (oldValue: string) => string); | ||
mutate.classList("div.greeting", classes => classes.add("new-class")); | ||
mutate.attr(".get-started", "title", oldVal => "This is my new title attribute"); | ||
// Example | ||
mutate.attribute(".link", "href", (href) => href + "?foo"); | ||
``` | ||
### Declarative Mutations | ||
### declarative | ||
If you don't need the full flexibility required by callback functions or you need to serialize the list of mutations in JSON, you can use the `declarative` method: | ||
Mutate the html, classes, or attributes using a declarative syntax instead of callbacks. | ||
Perfect for serialization. | ||
```ts | ||
// Signature | ||
mutate.declarative({ | ||
selector: "h1", | ||
action: "set", | ||
attribute: "html", | ||
value: "hello world" | ||
selector: string, | ||
action: "set" | "append" | "remove", | ||
attribute: "html" | "class" | string, | ||
value: string | ||
}); | ||
// Examples | ||
const mutations = [ | ||
{ | ||
selector: "h1", | ||
action: "set", | ||
attribute: "html", | ||
value: "new text" | ||
}, | ||
{ | ||
selector: ".get-started", | ||
action: "remove", | ||
attribute: "class", | ||
value: "green" | ||
}, | ||
{ | ||
selector: "a", | ||
action: "append", | ||
attribute: "href", | ||
value: "?foo" | ||
} | ||
]; | ||
mutations.forEach(m=>mutate.declarative(m)); | ||
``` | ||
There are 3 supported "actions": `append`, `set`, and `remove`. The `remove` action can only be used with the `class` attribute. | ||
## How it Works | ||
When you call `mutate`, we start watching the document for elements matching the selector to appear. We do this with a single shared MutationObserver on the body. | ||
When you create a mutation, we start watching the document for elements matching the selector to appear. We do this with a single shared MutationObserver on the body. | ||
When a matching element is found, we attach a separate MutationObserver filtered to the exact attribute being mutated. If an external change happens (e.g. from a React render), we re-apply your mutation on top of the new baseline value. | ||
When `stop` is called, we undo the change and go back to the last externally set value. We also disconnect the element's MutationObserver to save resources. | ||
When `revert` is called, we undo the change and go back to the last externally set value. We also disconnect the element's MutationObserver to save resources. | ||
@@ -95,0 +157,0 @@ ## Pausing / Resuming the Global MutationObserver |
@@ -282,5 +282,4 @@ const validAttributeName = /^[a-zA-Z:_][a-zA-Z0-9:_.-]*$/; | ||
const existingEls = new Set(mutation.elements); | ||
const newElements: Set<Element> = new Set(); | ||
const nodes = document.body.querySelectorAll(mutation.selector); | ||
const nodes = document.querySelectorAll(mutation.selector); | ||
nodes.forEach(el => { | ||
@@ -329,3 +328,3 @@ newElements.add(el); | ||
refreshAllElementSets(); | ||
observer.observe(document.body, { | ||
observer.observe(document.documentElement, { | ||
childList: true, | ||
@@ -332,0 +331,0 @@ subtree: true, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
117587
186