自定义元素不是 setting/getting 属性
custom elements not setting/getting attributes
如果我创建两个自定义元素并在另一个元素中创建其中一个,则属性不适用于第一个元素的新子元素。
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
const b = document.createElement('x-bar');
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
我希望我的输出是
value #0
value #1
value #2
因为我是这样设置属性的b.setAttribute('val', value #${i});
但我得到了 3 倍 no value
关于为什么会这样的任何意见? and/or如何解决,谢谢!
在 构造函数被调用之前,您不会设置属性;注意日志记录:
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
console.log("In constructor, val = " + val);
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
您需要将呈现逻辑移出构造函数,以便您可以考虑属性集 post-构造。也许通过覆盖 setAttribute
:
class Bar extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
this.wrapper = document.createElement('div');
this.render(); // Though calling methods from the constructor isn't ideal
shadow.appendChild(this.wrapper);
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === "val") {
this.render();
}
}
render() {
const val = this.getAttribute('val') || 'no value';
this.wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
从构造函数中调用方法并不理想,不过,您可能需要 fiddle 一点
大多数人都不知道 Web 组件的构造函数规则:
官方文档如下:
https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance
总结为:
您的构造函数代码:
- 必须将对 super() 的无参数调用作为构造函数中的第一个语句。
- 构造函数中的任何地方都不能有 return 语句。
- 不得调用 document.write() 或 document.open()。
- 不得检查元素的属性。
- 不得更改或添加任何属性或子项。
一般来说,构造函数应该用于设置初始状态和默认值,并设置事件侦听器和影子根。
总的来说我同意@T.J。 Crowder,但我会对 Bar
对象做一个小修改:
class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
connectedCallback() {
// Render the initial value
// when this element is placed into the DOM
render(this.getAttribute('val'));
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then re-render this element
render(newVal);
}
}
render(val = 'no value') {
this.shadowRoot.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
这里使用了attributeChangedCallback
和observedAttributes
的标准。虽然覆盖 setAttribute
功能有效,但它不是面向未来的。如果 setAttribute
的 API 将来要更改,那么您需要记住修复您的组件。对许多组件执行此操作会增加开发人员的大量债务。
class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
// Render the blank DOM
this.shadowRoot.innerHTML = '<div class="bar"><span>no value</span><div>';
this._span = this.shadowRoot.querySelector('span');
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then insert the value into the `<span>`
this._span.textContent = newVal || 'no value';
// OR: this._span.innerHTML = newVal || 'no value';
// But make sure someone has not tried to hit you
// with a script attack.
}
}
get val() {
return this._span.textContent;
}
set val(newVal) {
if (newVal == null || newVal === false || newVal === '') {
this.removeAttribute('val');
}
else {
this.setAttribute('val', newVal);
}
}
}
customElements.define('x-bar', Bar);
第二种方法没有重新呈现整个 DOM 它只是将修改后的属性值插入到 <span>
标记中。
它还提供属性,因此您可以通过属性以及通过 JavaScript:
设置值
var el = document.querySelector('x-bar');
if (el) {
el.val = "A New String";
setTimeout(()=>el.val = '';,2000);
}
如果我创建两个自定义元素并在另一个元素中创建其中一个,则属性不适用于第一个元素的新子元素。
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
const b = document.createElement('x-bar');
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
我希望我的输出是
value #0
value #1
value #2
因为我是这样设置属性的b.setAttribute('val', value #${i});
但我得到了 3 倍 no value
关于为什么会这样的任何意见? and/or如何解决,谢谢!
在 构造函数被调用之前,您不会设置属性;注意日志记录:
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
console.log("In constructor, val = " + val);
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
您需要将呈现逻辑移出构造函数,以便您可以考虑属性集 post-构造。也许通过覆盖 setAttribute
:
class Bar extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
this.wrapper = document.createElement('div');
this.render(); // Though calling methods from the constructor isn't ideal
shadow.appendChild(this.wrapper);
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === "val") {
this.render();
}
}
render() {
const val = this.getAttribute('val') || 'no value';
this.wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
从构造函数中调用方法并不理想,不过,您可能需要 fiddle 一点
大多数人都不知道 Web 组件的构造函数规则:
官方文档如下:
https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance
总结为:
您的构造函数代码:
- 必须将对 super() 的无参数调用作为构造函数中的第一个语句。
- 构造函数中的任何地方都不能有 return 语句。
- 不得调用 document.write() 或 document.open()。
- 不得检查元素的属性。
- 不得更改或添加任何属性或子项。
一般来说,构造函数应该用于设置初始状态和默认值,并设置事件侦听器和影子根。
总的来说我同意@T.J。 Crowder,但我会对 Bar
对象做一个小修改:
class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
connectedCallback() {
// Render the initial value
// when this element is placed into the DOM
render(this.getAttribute('val'));
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then re-render this element
render(newVal);
}
}
render(val = 'no value') {
this.shadowRoot.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
这里使用了attributeChangedCallback
和observedAttributes
的标准。虽然覆盖 setAttribute
功能有效,但它不是面向未来的。如果 setAttribute
的 API 将来要更改,那么您需要记住修复您的组件。对许多组件执行此操作会增加开发人员的大量债务。
class Bar extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
// Render the blank DOM
this.shadowRoot.innerHTML = '<div class="bar"><span>no value</span><div>';
this._span = this.shadowRoot.querySelector('span');
}
static get observedAttributes() {
// Indicate that we want to be notified
// when the `val` attribute is changed
return ['val'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal != newVal) {
// If the value for the `val` attribute has changed
// then insert the value into the `<span>`
this._span.textContent = newVal || 'no value';
// OR: this._span.innerHTML = newVal || 'no value';
// But make sure someone has not tried to hit you
// with a script attack.
}
}
get val() {
return this._span.textContent;
}
set val(newVal) {
if (newVal == null || newVal === false || newVal === '') {
this.removeAttribute('val');
}
else {
this.setAttribute('val', newVal);
}
}
}
customElements.define('x-bar', Bar);
第二种方法没有重新呈现整个 DOM 它只是将修改后的属性值插入到 <span>
标记中。
它还提供属性,因此您可以通过属性以及通过 JavaScript:
设置值var el = document.querySelector('x-bar');
if (el) {
el.val = "A New String";
setTimeout(()=>el.val = '';,2000);
}