Skip to content

Commit

Permalink
feat: observe for attribute changes
Browse files Browse the repository at this point in the history
  • Loading branch information
infodusha committed Aug 24, 2023
1 parent ab21e56 commit f616803
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 13 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ So later you can use include your template with:
* Make conditional elements
* Optionally enable shadow root
* Partial style encapsulation (when not a shadow root mode)
* Watch for attribute changes from js

# TODO

* Watch for attribute changes (from js)
* Extended script support
* Full style encapsulation (when not a shadow root mode)
* Extended script support
* Full slot support

# License
Expand Down
2 changes: 1 addition & 1 deletion example/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@

<script>
setTimeout(() => {
this.name = 'Reset';
this.setAttribute('name', 'Censored');
}, 3000);
</script>
74 changes: 64 additions & 10 deletions src/define-custom-element.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {ErrorStackModifier} from './error-stack-modifier.js';


export function defineCustomElement(definedElement) {
const template = definedElement.querySelector('template');
const useShadow = template.hasAttribute('data-shadow');
Expand All @@ -12,16 +13,31 @@ export function defineCustomElement(definedElement) {
}

class DefineHTMLElement extends HTMLElement {
#observedProperties = new Set();

#attributeChangeObserver;

constructor() {
super();
const onAttributeChange = this.#onAttributeChange.bind(this);
this.#attributeChangeObserver = new MutationObserver(onAttributeChange);
this.#attach();
this.#setAttrs();
if (useShadow) {
this.#setShadowStyles();
}
this.#observeAttributes();
this.#execScripts();
}

connectedCallback() {
this.#attributeChangeObserver.observe(this, { attributes: true });
}

disconnectedCallback() {
this.#observedProperties.clear();
this.#attributeChangeObserver.disconnect()
}

#getContent() {
const content = template.content.cloneNode(true);
for (const element of content.querySelectorAll(`[data-if]`)) {
Expand Down Expand Up @@ -56,17 +72,15 @@ export function defineCustomElement(definedElement) {
}
}

#setAttrs() {
#applyAttr(name, value) {
const root = useShadow ? this.shadowRoot : this;
for (const name of this.getAttributeNames()) {
const value = this.getAttribute(name);
for (const element of root.querySelectorAll(`[data-attr='${name}']`)) {
if (element.childNodes) {
// TODO handle case when there are already nodes inside
}
element.innerText = value;
for (const element of root.querySelectorAll(`[data-attr='${name}']`)) {
if (element.childNodes) {
// TODO handle case when there are already nodes inside
}
element.innerText = value;
}
// TODO add support for data-if and data-if-not when changed via js
}

#setShadowStyles() {
Expand Down Expand Up @@ -96,6 +110,46 @@ export function defineCustomElement(definedElement) {
}
}
}

#observeAttributes() {
for (const name of this.getAttributeNames()) {
this.#makeObservedProperty(name);
}
}

#makeObservedProperty(name) {
if (this.#observedProperties.has(name)) {
return;
}
let value = this.getAttribute(name);
this.#applyAttr(name, value);
Object.defineProperty(this, name, {
get() {
return value;
},
set(newValue) {
value = newValue;
this.#applyAttr(name, newValue);
},
enumerable: true,
configurable: true,
});
this.#observedProperties.add(name);
}

#onAttributeChange(mutationList) {
for (const mutation of mutationList) {
if (mutation.type !== 'attributes') {
throw new Error('Unexpected');
}
const { attributeName } = mutation;
if (this.#observedProperties.has(attributeName)) {
this[attributeName] = this.getAttribute(attributeName);
} else {
this.#makeObservedProperty(attributeName);
}
}
}
}

customElements.define(selector, DefineHTMLElement);
Expand All @@ -122,4 +176,4 @@ function setEmulatedStyles(styles, selector) {
document.head.appendChild(element);
URL.revokeObjectURL(url);
}
}
}

0 comments on commit f616803

Please sign in to comment.