英文:
Why would this web component not show?
问题
我有一些JS代码,它创建一个Web组件,然后将其添加到一个非常基本的HTML页面中。
class WordCount extends HTMLParagraphElement {
constructor() {
// 在构造函数中始终首先调用super
super();
// 计算元素的父元素中的单词数
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
// 创建影子根
const shadow = this.attachShadow({ mode: "open" });
// 创建文本节点并将单词计数添加到其中
const text = document.createElement("span");
text.textContent = count;
// 将其附加到影子根
shadow.appendChild(text);
// 当元素内容更改时更新计数
setInterval(function () {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}
window.customElements.define("word-count", WordCount, { extends: "p" });
var c1 = document.getElementById('component1');
var header= document.createElement('h1')
header.innerText="Web Component 1"
c1.appendChild(header)
var article = document.createElement('article')
article.setAttribute('contenteditable', '')
c1.appendChild(article)
var h2 = document.createElement('h2');
h2.innerText = "Sample Heading";
article.appendChild(h2);
var p1 = document.createElement('p')
p1.innerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
article.appendChild(p1)
var p2 = document.createElement('p')
p2.innerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
article.appendChild(p2)
var p3 = document.createElement('p')
p3.setAttribute('is', 'word-count')
article.appendChild(p3)
customElements.upgrade(p3)
这是一些非常基本的HTML,它附加到其中:
<div id="component1">
</div>
最终,我对为什么单词计数不显示感到困惑。这基于MDN上的word count webcomponent示例。唯一的区别是我是使用JS构建HTML,而不是直接使用HTML。我尝试了一些方法,比如将JS包装在DOMContentLoaded事件侦听器中,或者使用或不使用customElements.upgrade()方法,但似乎都没有什么区别。
英文:
I have some JS that creates a web component and then proceeds to add it to a very basic HTML page
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// count words in element's parent element
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
// Create a shadow root
const shadow = this.attachShadow({ mode: "open" });
// Create text node and add word count to it
const text = document.createElement("span");
text.textContent = count;
// Append it to the shadow root
shadow.appendChild(text);
// Update count when element content changes
setInterval(function () {
const count = `Words: ${countWords(wcParent)}`;
text.textContent = count;
}, 200);
}
}
window.customElements.define("word-count", WordCount, { extends: "p" });
var c1 = document.getElementById('component1');
var header= document.createElement('h1')
header.innerText="Web Component 1"
c1.appendChild(header)
var article = document.createElement('article')
article.setAttribute('contenteditable', '')
c1.appendChild(article)
var h2 = document.createElement('h2');
h2.innerText = "Sample Heading";
article.appendChild(h2);
var p1 = document.createElement('p')
p1.innerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
article.appendChild(p1)
var p2 = document.createElement('p')
p2.innerText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
article.appendChild(p2)
var p3 = document.createElement('p')
p3.setAttribute('is', 'word-count')
article.appendChild(p3)
customElements.upgrade(p3)
There is some VERY basic HTML that this attaches on to
<div id="component1">
</div>
Ultimately I'm puzzled why the word count doesn't show. This is based upon the word count webcomponent example from mdn. The only difference here is that I'm building the HTML using JS rather than using the HTML directly.
I tried a few things, such as waiting until the HTML loads by wrapping the js in the DOMContentLoaded
event listener, or using or not using the customElements.upgrade()
method but that didn't seem to make a difference.
答案1
得分: 1
您的 WordCount
类扩展了 HTMLParagraphElement
,这意味着它应该与 <p>
标签的实例一起使用作为自定义内置元素。这需要您首先使用 window.customElements.define
方法定义此自定义元素,并使用 extends
选项指定要扩展的元素,因此 is='word-count'
不是有效的自定义元素定义。在您定义类之后,添加以下行应该解决问题。
window.customElements.define('word-count', WordCount, { extends: 'p' });
您还可以使用 connectedCallback
生命周期方法,它在自定义元素连接到文档的 DOM 时调用,以启动您的字数统计功能。尝试将类定义更改为以下内容:
class WordCount extends HTMLParagraphElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.updateWordCount();
this.intervalId = setInterval(() => this.updateWordCount(), 200);
}
disconnectedCallback() {
clearInterval(this.intervalId);
}
updateWordCount() {
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text.split(/\s+/g).filter(a => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
this.shadowRoot.textContent = count;
}
}
以上为翻译好的内容,没有其他内容。
英文:
Your WordCount
class extends HTMLParagraphElement
, which implies it should be used with an instance of <p>
tag as a customized built-in element. This requires you to first define this customized element using the window.customElements.define
method, and to specify the element to extend using the extends option hence is='word-count'
is not a valid custom element definition. Adding this line after you define the class should solve the issue.
window.customElements.define('word-count', WordCount, { extends: 'p' });
You can also use connectedCallback
lifecycle method, which is called when the custom element is connected to the document's DOM, to start your word count functionality. Try altering your class definition to something like this:
class WordCount extends HTMLParagraphElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.updateWordCount();
this.intervalId = setInterval(() => this.updateWordCount(), 200);
}
disconnectedCallback() {
clearInterval(this.intervalId);
}
updateWordCount() {
const wcParent = this.parentNode;
function countWords(node) {
const text = node.innerText || node.textContent;
return text.split(/\s+/g).filter(a => a.trim().length > 0).length;
}
const count = `Words: ${countWords(wcParent)}`;
this.shadowRoot.textContent = count;
}
}
答案2
得分: 0
Here are the translated portions of your text:
-
// Always call super first in constructor
该文档错误。
您可以调用任何JS,只是不能调用'this'范围,因为super()
设置并返回了'this'范围
请查看我的Dev.to帖子Web组件#102,学完Web组件#101后的5个教训 -
const wcParent = this.parentNode;
在构造函数中,不能(总是)这样做,构造函数阶段没有元素DOM。如果您的脚本将在页面中没有DOM之前运行,这一行将返回空。
请使用connectedCallback
;这是当自定义元素连接到DOM时触发的,但请注意它在开标签上触发,因此内部的任何HTML尚未被解析。 -
customElements.define('word-count', WordCount, { extends: 'p' });
在Safari中不受支持。自2016年以来,苹果已经声明他们不会实现_Customized Built-In Elements_,因为这些元素不符合(面向对象编程)Liskov原则。 -
setInterval
有点粗糙,只会浪费CPU周期。
您应该在这里使用MutationObserver API以仅监视HTML中的更改。 -
一个字/字母计数Web组件可以这样实现:
请注意,这里不需要_shadowDOM_;当您想要使用全局CSS进行样式设置时,它会成为阻碍。
<article contenteditable> <h3>糟糕的Web组件编写</h3> <p>除了设置外,主要问题是HTML在这些组件的设计中未受到适当的尊重。它们没有尽可能紧密地设计为标准HTML元素,而是期望为它们编写JS才能执行任何操作。HTML仅被视为一种缩写,或者更糟糕的是,仅被视为一个标记,用来指示元素在DOM中的位置,所有参数都通过JS传递。</p> <text-count letters words></text-count> </article> <script> customElements.define('text-count', class extends HTMLElement { constructor() { super().attachShadow({mode: 'open'}) .innerHTML = `<style>span{font-weight:bold;background:beige;color:black}</style>` + `<span><!--counter--></span>`; } connectedCallback() { let article = this.closest("article"); if (article) { let updatecount /*function*/ = () => { let text = article.innerText.trim();//需要微调,还计算<text-count> this.shadowRoot.querySelector("span").innerText = (this.hasAttribute("letters") ? text.length + " 字母 " : "") + (this.hasAttribute("words") ? text.split(/\s+/g).length + " 单词" : ""); } article.onkeyup = (evt) => updatecount(); updatecount(); } else { console.warn("我找不到父级<article>!"); } } }); </script>
英文:
First some corrections:
-
// Always call super first in constructor
The documenation is wrong on this one.
You can call any JS, just not the 'this' scope, becausesuper()
SETS and RETURNS the 'this' scope
See my Dev.to post Web Components #102, 5 more lessons after #101 -
const wcParent = this.parentNode;
You can't (always) do this in theconstructor
, there is no Element DOM in theconstructor
phase. If your script will run before there is any DOM in the page this line will return nothing.
Use theconnectedCallback
; that is when then Custom Element is connected to the DOM, but do note it fires on the opening tag, thus any HTML inside has not been parsed yet. -
customElements.define('word-count', WordCount, { extends: 'p' });
is not supported in Safari. Since 2016 Apple has stated they will not implement Customized Built-In Elements, because those elements do not adhere to the (OOP) Liskov Principle. -
setInterval
is a bit blunt; just waisting CPU cycles.
You want to use the MutationObserver API here to only monitor changes in HTML.
A word/letter count Web Component can be done like:
Note the shadowDOM is not required here; a hinderance when you want to style with global CSS
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
<article contenteditable>
<h3>Badly written Web Components</h3>
<p>Besides setup, the main problem is that HTML is not treated with the
appropriate respect in the design of these components. They are not designed as closely as possible to standard HTML elements, but expect JS to be written for them to do anything. HTML is simply treated as a shorthand, or worse, as merely a marker to indicate where the element goes in the DOM, with all parameters passed in via JS.</p>
<text-count letters words></text-count>
</article>
<script>
customElements.define('text-count', class extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'})
.innerHTML=`<style>span{font-weight:bold;background:beige;color:black}</style>`+
`<span><!--counter--></span>`;
}
connectedCallback() {
let article = this.closest("article");
if (article) {
let updatecount /*function*/ = () => {
let text = article.innerText.trim();//needs tweaking, also counts <text-count>
this.shadowRoot.querySelector("span").innerText =
(this.hasAttribute("letters") ? text.length + " letters " : "") +
(this.hasAttribute("words") ? text.split(/\s+/g).length + " words" : "");
}
article.onkeyup = (evt) => updatecount();
updatecount();
} else {
console.warn("I can't find a parent <article>!");
}
}
});
</script>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论