我的目标是动态设置
observedAttributes
,因此我的 Web 组件将仅监视遵循某种模式的属性,例如冒号 (:attr) <my-element static="value1" :dynamic=${dynamic}/>
<my-element>
应仅为属性 observedAttributes
设置
:dynamic
问题是
static get observedAttributes()
甚至在 this
之前运行,解释于 https://andyogo.github.io/custom-element-reactions-diagram/
所以这行不通
static get observedAttributes() {
return this.getAttributeNames().filter((item) => item.startsWith(':'));
}
当然也没有
constructor() {
super();
this._observedAttributes = this.getAttributeNames().filter((item) => item.startsWith(':'));
}
static get observedAttributes() {
return this._observedAttributes;
}
谢谢!
<!DOCTYPE html>
<body>
<my-element static="value1" :dynamic="value2" ></my-element>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
this._observedAttributes= this.getAttributeNames().filter((item) => item.startsWith(':'));
console.log('observedAttributes',this._observedAttributes);
}
static get observedAttributes() {
return this._observedAttributes;
}
attributeChangedCallback(name, oldValue, newValue) {
console.log(name, oldValue, newValue); //doesn't log anything
}
}
customElements.define("my-element", MyElement);
setTimeout(() => {
console.log('setting dynamic attribute. This should trigger attributeChangedCallback. But no.');
document.querySelector('my-element').setAttribute(':dynamic', 'value3');
}, 2000);
</script>
</body>
</html>
您可以使用以下垫片来添加在元素的每个实例上动态观察属性变化的功能。您可以定义默认情况下在所有实例上观察到的属性,也可以在运行时添加仅在特定实例上观察到的属性。
class BaseClass extends HTMLElement {
constructor({stat = BaseClass} = {}) {
super()
// console.log('constructor')
this.#stat = stat
this.#setAttribute = this.setAttribute
this.setAttribute = function(name, value){
// console.log('attribute', name, this.observedAttributes, (this.observedAttributes.includes(name)))
if (this.observedAttributes.includes(name)){
// do your stuff
// console.log('attribute includes')
this.attributeChangedCallback(name, this.getAttribute(name), value)
}
else {
// do what you want
}
this.#setAttribute(name, value)
}
}
#observedAttributes = []
get observedAttributes(){
return [...this.#stat.observedAttributes, ...this.#observedAttributes]
}
#setAttribute = null
#stat = null
addObservedProp(name){
this.#observedAttributes.push(name)
}
connectedCallback() {}
disconnectedCallback() {}
attributeChangedCallback(name, oldValue, newValue) {
console.log('change', name, oldValue, newValue)
}
adoptedCallback() {}
static get observedAttributes() {
return ['a', 'b', 'c']
}
}
window.customElements.define('base-com', BaseClass)
const test = document.getElementById('test')
test.setAttribute('d', 'status 1')
test.addObservedProp('d')
test.setAttribute('d', 'status 2')
console.log(test)
<base-com id="test">Base com</base-com>
按照 @Danny '365CSI' Engelman 的建议,我查看了 MutationObserver API 并提出了一个简单的解决方案,我认为它提供的不仅仅是
observedAttributes
<my-element static="value1" :dynamic="value2"></my-element>
<script>
class MyElement extends HTMLElement {
constructor() {
super();
}
mutationObserverCallback(mutationList, observer) {
for (const mutation of mutationList) {
if (mutation.type === 'attributes' &&
mutation.attributeName.startsWith(':') &&
mutation.oldValue !== mutation.target.getAttribute(mutation.attributeName)) {
console.log(`The dynamic ${mutation.attributeName} attribute was modified.`);
}
}
}
connectedCallback() {
this.mutationObserver = new MutationObserver(this.mutationObserverCallback);
this.mutationObserver.observe(this, {
attributes: true,
attributeOldValue: true
});
}
disconnectedCallback() {
this.mutationObserver.disconnect();
}
}
customElements.define("my-element", MyElement);
setTimeout(() => {
console.log('setting dynamic attribute for :dynamic and static attributes. This should trigger mutationObserverCallback for :dynamic only.');
// ▼ will trigger mutationObserverCallback
document.querySelector('my-element').setAttribute(':dynamic', 'value3');
// ▼ will not trigger mutationObserverCallback
document.querySelector('my-element').setAttribute('static', 'value4');
}, 200);
</script>