admin管理员组文章数量:1403443
if i create two custom elements and create one of them inside the other one, the attributes are not working on the new childs of the first element.
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>
if i create two custom elements and create one of them inside the other one, the attributes are not working on the new childs of the first element.
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>
im expecting that my output would be
value #0
value #1
value #2
as i have set the attr like this b.setAttribute('val', value #${i});
but im getting 3x no value
Any inputs on why that is? and/or how to fix it, Thanks!
Share Improve this question asked May 24, 2018 at 11:52 kejakeja 1,3631 gold badge15 silver badges21 bronze badges 1- Possible duplicate of Cannot access attributes of a custom element from its constructor – Supersharp Commented May 24, 2018 at 21:58
2 Answers
Reset to default 6Most people are unaware of the rules of a constructor for Web Components:
Here are the official docs:
https://w3c.github.io/webponents/spec/custom/#custom-element-conformance
The summary is:
Your constructor code:
- must have a parameter-less call to super() as first statement in constructor.
- must not have a return statement anywhere in constructor.
- must not call document.write() or document.open().
- must not inspect element's attributes.
- must not change or add any attributes or children.
In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.
In general I agree with @T.J. Crowder, but I would make one minor modification to the Bar
object:
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);
This used the standard of attributeChangedCallback
and observedAttributes
. While overriding the setAttribute
functionality works it is not future proof. If the API for setAttribute
were to change, in the future, then you would need to remember to fix your ponent. And doing this with many ponents racks up a lot of developer debt.
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);
This second method done not re-render the entire DOM it simple inserts the modified attribute value into the <span>
tag.
It also provides properties so you can set the value through the attribute as well as through JavaScript:
var el = document.querySelector('x-bar');
if (el) {
el.val = "A New String";
setTimeout(()=>el.val = '';,2000);
}
You're not setting the attribute until after the constructor has already been called; note the logging:
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>
You'll need to move your rendering logic out of the constructor so you can take attributes set post-construction into account. Perhaps by overriding 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>
Calling methods from the constructor isn't ideal, though, you may want to fiddle with that a bit
本文标签: javascriptcustom elements not settinggetting attributesStack Overflow
版权声明:本文标题:javascript - custom elements not settinggetting attributes - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744329591a2600898.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论