规范中提到的自定义元素示例的最小实现是什么?
What is the minimal implementation for custom elements example mentioned in the specifications?
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example:htmlelement
在规范中,他们提供了 创建自主自定义元素 的示例。但是,他们为读者留下了 _updateRendering() 方法实现。
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
// Left as an exercise for the reader. But, you'll probably want to
// check this.ownerDocument.defaultView to see if we've been
// inserted into a document with a browsing context, and avoid
// doing any work if not.
}
}
已提出问题以提供剩余的实现,以便更好地理解主题并快速继续。
问题:https://github.com/whatwg/html/issues/3029
我们可以放什么代码来获得所需的功能?
这是实现相同要求的完整代码:
<!DOCTYPE html>
<html lang="en">
<body>
<flag-icon country="in"></flag-icon><br>
<flag-icon country="nl"></flag-icon><br>
<flag-icon country="us"></flag-icon><br>
</body>
<script>
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
//**remaining code**
if (this.ownerDocument.defaultView && !this.hasChildNodes()) {
var flag = document.createElement("img");
flag.src = "https://flagcdn.com/24x18/" + this._countryCode + ".png";
this.appendChild(flag);
}
}
}
customElements.define("flag-icon", FlagIcon);
</script>
</html>
注意:加载图片可能需要一些时间,具体取决于网速。
如果我遗漏了什么,请告诉我。
您的解决方案失败,一旦设置了标志,您将永远无法更改该值。
那个“练习”很老了.. 很老了.. 并且试图展示自定义元素可以做的一切。
这是完全错误的.. 关键是当 attributeChanged
运行时,old/new 值是什么
并且 attributeChangedCallback
运行 BEFORE connectedCallback
;这就是添加 https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected 的原因。
您的代码在 attributeChangedCallback
中获取了 3 个参数,但您不能对它们执行任何操作,因为执行总是转到 _updateRendering
方法。
如果练习的目的是在观察到 attributes
变化时学习,我会使用:
代码也可以在 JSFiddle 中找到:https://jsfiddle.net/dannye/43ud1wvn/
<script>
class FlagIcon extends HTMLElement {
static observedAttributes = ["country"];
log(...args) {
document.body.appendChild(document.createElement("div"))
.innerHTML = `${this.id} - ${args.join` `}`;
}
attributeChangedCallback(name, oldValue, newValue) {
this.log("<b>attributeChangedCallback:</b>", `("${name}" , "${oldValue}", "${newValue}" )`);
if (this.isConnected) {
if (newValue == oldValue) this.log(`Don't call SETTER ${name} again!`);
else this[name] = newValue; // call SETTER
} else this.log("is not a DOM element yet!!!");
}
connectedCallback() {
this.log("<b>connectedCallback</b>, this.img:", this.img || "not defined");
this.img = document.createElement("img");
this.append(this.img); // append isn't available in IE11
this.country = this.getAttribute("country") || "EmptyCountry";
}
get country() { // the Attribute is the truth, no need for private variables
return this.getAttribute("country");
}
set country(v) {
this.log("SETTER country:", v);
// Properties and Attributes are in sync,
// but setAttribute will trigger attributeChanged one more time!
this.setAttribute("country", v);
if (this.img) this.img.src = `//flagcdn.com/20x15/${v}.png`;
else this.log("can't set country", v);
}
}
customElements.define("flag-icon", FlagIcon);
document.body.onclick = () => {
flag1.country = "nl";
flag2.setAttribute("country", "nl");
}
</script>
<flag-icon id="flag1" country="in"></flag-icon><br>
<flag-icon id="flag2" country="us"></flag-icon><br>
这只是一种方法,这完全取决于 what/when/how 您的自定义元素需要进行更新。
定义 CustomElement 时也很重要; before 或 after DOM 被解析。大多数开发人员只是在他们的脚本上敲打 deferred
或 method
,而不理解它意味着什么。
始终使用定义自定义元素之前的代码测试您的 Web 组件,它在 DOM 中使用。
真实世界<flag-icon>
Web 组件
待优化:
<script>
customElements.define("flag-icon", class extends HTMLElement {
static observedAttributes = ["country"];
attributeChangedCallback() {
this.isConnected && this.connectedCallback();
}
connectedCallback() {
this.img = this.img || this.appendChild(document.createElement("img"));
this.img.src = `//flagcdn.com/120x90/${this.country}.png`;
}
get country() {
return this.getAttribute("country") || console.error("Missing country attribute",this);
}
set country(v) {
this.setAttribute("country", v);
}
});
</script>
<flag-icon id="flag1" country="gb"></flag-icon>
<flag-icon id="flag2" country="eu"></flag-icon>
或者根本没有外部图像
使用 FlagMeister Web Component 创建所有 SVG 客户端
<script src="//flagmeister.github.io/elements.flagmeister.min.js"></script>
<div style="display:grid;grid-template-columns: repeat(3,1fr);gap:1em">
<flag-jollyroger></flag-jollyroger>
<flag-un></flag-un>
<flag-lgbt></flag-lgbt>
</div>
https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-autonomous-example:htmlelement
在规范中,他们提供了 创建自主自定义元素 的示例。但是,他们为读者留下了 _updateRendering() 方法实现。
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
// Left as an exercise for the reader. But, you'll probably want to
// check this.ownerDocument.defaultView to see if we've been
// inserted into a document with a browsing context, and avoid
// doing any work if not.
}
}
已提出问题以提供剩余的实现,以便更好地理解主题并快速继续。
问题:https://github.com/whatwg/html/issues/3029
我们可以放什么代码来获得所需的功能?
这是实现相同要求的完整代码:
<!DOCTYPE html>
<html lang="en">
<body>
<flag-icon country="in"></flag-icon><br>
<flag-icon country="nl"></flag-icon><br>
<flag-icon country="us"></flag-icon><br>
</body>
<script>
class FlagIcon extends HTMLElement {
constructor() {
super();
this._countryCode = null;
}
static observedAttributes = ["country"];
attributeChangedCallback(name, oldValue, newValue) {
// name will always be "country" due to observedAttributes
this._countryCode = newValue;
this._updateRendering();
}
connectedCallback() {
this._updateRendering();
}
get country() {
return this._countryCode;
}
set country(v) {
this.setAttribute("country", v);
}
_updateRendering() {
//**remaining code**
if (this.ownerDocument.defaultView && !this.hasChildNodes()) {
var flag = document.createElement("img");
flag.src = "https://flagcdn.com/24x18/" + this._countryCode + ".png";
this.appendChild(flag);
}
}
}
customElements.define("flag-icon", FlagIcon);
</script>
</html>
注意:加载图片可能需要一些时间,具体取决于网速。
如果我遗漏了什么,请告诉我。
您的解决方案失败,一旦设置了标志,您将永远无法更改该值。
那个“练习”很老了.. 很老了.. 并且试图展示自定义元素可以做的一切。
这是完全错误的.. 关键是当 attributeChanged
运行时,old/new 值是什么
并且 attributeChangedCallback
运行 BEFORE connectedCallback
;这就是添加 https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected 的原因。
您的代码在 attributeChangedCallback
中获取了 3 个参数,但您不能对它们执行任何操作,因为执行总是转到 _updateRendering
方法。
如果练习的目的是在观察到 attributes
变化时学习,我会使用:
代码也可以在 JSFiddle 中找到:https://jsfiddle.net/dannye/43ud1wvn/
<script>
class FlagIcon extends HTMLElement {
static observedAttributes = ["country"];
log(...args) {
document.body.appendChild(document.createElement("div"))
.innerHTML = `${this.id} - ${args.join` `}`;
}
attributeChangedCallback(name, oldValue, newValue) {
this.log("<b>attributeChangedCallback:</b>", `("${name}" , "${oldValue}", "${newValue}" )`);
if (this.isConnected) {
if (newValue == oldValue) this.log(`Don't call SETTER ${name} again!`);
else this[name] = newValue; // call SETTER
} else this.log("is not a DOM element yet!!!");
}
connectedCallback() {
this.log("<b>connectedCallback</b>, this.img:", this.img || "not defined");
this.img = document.createElement("img");
this.append(this.img); // append isn't available in IE11
this.country = this.getAttribute("country") || "EmptyCountry";
}
get country() { // the Attribute is the truth, no need for private variables
return this.getAttribute("country");
}
set country(v) {
this.log("SETTER country:", v);
// Properties and Attributes are in sync,
// but setAttribute will trigger attributeChanged one more time!
this.setAttribute("country", v);
if (this.img) this.img.src = `//flagcdn.com/20x15/${v}.png`;
else this.log("can't set country", v);
}
}
customElements.define("flag-icon", FlagIcon);
document.body.onclick = () => {
flag1.country = "nl";
flag2.setAttribute("country", "nl");
}
</script>
<flag-icon id="flag1" country="in"></flag-icon><br>
<flag-icon id="flag2" country="us"></flag-icon><br>
这只是一种方法,这完全取决于 what/when/how 您的自定义元素需要进行更新。
定义 CustomElement 时也很重要; before 或 after DOM 被解析。大多数开发人员只是在他们的脚本上敲打 deferred
或 method
,而不理解它意味着什么。
始终使用定义自定义元素之前的代码测试您的 Web 组件,它在 DOM 中使用。
真实世界<flag-icon>
Web 组件
待优化:
<script>
customElements.define("flag-icon", class extends HTMLElement {
static observedAttributes = ["country"];
attributeChangedCallback() {
this.isConnected && this.connectedCallback();
}
connectedCallback() {
this.img = this.img || this.appendChild(document.createElement("img"));
this.img.src = `//flagcdn.com/120x90/${this.country}.png`;
}
get country() {
return this.getAttribute("country") || console.error("Missing country attribute",this);
}
set country(v) {
this.setAttribute("country", v);
}
});
</script>
<flag-icon id="flag1" country="gb"></flag-icon>
<flag-icon id="flag2" country="eu"></flag-icon>
或者根本没有外部图像
使用 FlagMeister Web Component 创建所有 SVG 客户端
<script src="//flagmeister.github.io/elements.flagmeister.min.js"></script>
<div style="display:grid;grid-template-columns: repeat(3,1fr);gap:1em">
<flag-jollyroger></flag-jollyroger>
<flag-un></flag-un>
<flag-lgbt></flag-lgbt>
</div>