web找不到自定义组件方法
I wrote my first Custom Element using a CSS library called CSS Doodle. It’s an amazing app that uses Custom Elements to let you create stunning CSS animations. That unlocked a whole new desire to figure out how that thing works under the hood. So I decided to take a better look at Web Components in general, a topic many people asked me to write about.
我使用名为CSS DoodleCSS库编写了第一个自定义元素。 这是一个了不起的应用程序,它使用“自定义元素”来创建令人惊叹CSS动画。 这就释放了一种全新的渴望,即弄清楚该东西是如何工作的。 因此,我决定从总体上更好地研究Web组件,这是许多人要求我撰写的主题。
Custom Elements let us create new HTML tags.
使用自定义元素,我们可以创建新HTML标签。
I could not imagine why this could be a useful thing until I used that CSS Doodle library. After all we already have lots of tags.
在使用CSS Doodle库之前,我无法想象为什么这可能是有用的事情。 毕竟我们已经有很多标签。
This tutorial covers Custom Elements version 1, the latest release of the standard at the time of writing
本教程介绍了Custom Elements版本1,该标准是撰写本文时的最新版本
Using Custom Elements we can create a custom HTML tag with associated CSS and JavaScript.
使用自定义元素,我们可以创建具有关联CSS和JavaScript的自定义HTML标签。
It’s not an alternative to frameworks like React, Angular or Vue, but it’s a whole new concept.
它不是React,Angular或Vue等框架的替代品,而是一个全新的概念。
The window
global object exposes a customElements
property that gives us access to a CustomElementRegistry
object.
window
全局对象公开了customElements
属性,该属性使我们可以访问CustomElementRegistry
对象。
CustomElementRegistry
对象 (The CustomElementRegistry
object)
This object has several methods we can use to register Custom Elements and query Custom Elements already registered:
该对象有几种方法可用于注册自定义元素和查询已注册的自定义元素:
define()
used to define a new Custom Elementdefine()
用于定义新的自定义元素get()
used to get the constructor of a Custom Element (returnsundefined
if not existing)get()
用于获取自定义元素的构造函数(如果不存在,则返回undefined
)upgrade()
to upgrade a Custom Elementupgrade()
升级自定义元素whenDefined()
used to get the constructor of a Custom Element. Similarly toget()
, but it returns a promise instead, which resolves when the item is available.whenDefined()
用于获取自定义元素的构造函数。 与get()
类似,但是它返回一个promise,它在商品可用时解析。
如何创建自定义元素 (How to create a custom element)
Before we can call the window.customElements.define()
method, we must define a new HTML element by creating a new class that extends the HTMLElement built-in class:
在调用window.customElements.define()
方法之前,我们必须通过创建一个扩展HTMLElement内置类的新类来定义一个新HTML元素:
class CustomTitle extends HTMLElement {
//...
}
Inside the class constructor we’re going to use Shadow DOM to associate custom CSS, JavaScript and HTML to our new tag.
在类构造函数中,我们将使用Shadow DOM将自定义CSS,JavaScript和HTML关联到我们的新标记。
In this way, all we’ll see in the HTML is our tag, but this will encapsulate a lot of functionality.
这样,我们在HTML中将看到的只是我们的标签,但这将封装许多功能。
We start by initializing the constructor:
我们从初始化构造函数开始:
class CustomTitle extends HTMLElement {
constructor() {
super()
//...
}
}
Then, into it we call the attachShadow()
method of the HTMLElement by passing an object with the mode
property set to 'open'
. This property sets the encapsulation mode for the shadow DOM. If it’s open
we can access the shadowRoot
property of an element. If it’s closed, we can’t.
然后,通过传递mode
属性设置为'open'
的对象,调用HTMLElement的attachShadow()
方法。 此属性设置影子DOM的封装模式。 如果open
则可以访问元素的shadowRoot
属性。 如果关闭,我们不能。
Here’s how to do so:
这样做的方法如下:
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
//...
}
}
Some examples you’ll find use the syntax const shadowRoot = this.attachShadow(/* ... */)
but you can avoid so unless you set mode
to closed
, as you can always reference that object by calling this.shadowRoot
.
您会发现一些示例使用语法const shadowRoot = this.attachShadow(/* ... */)
但除非将mode
设置为closed
,否则可以避免这样做,因为您始终可以通过调用this.shadowRoot
引用该对象。
Which is what we’re going to do now, to set the innerHTML of it:
我们现在要做的是设置它的innerHTML:
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<h1>My Custom Title!</h1>
`
}
}
You can add as many tags as you want, you’re not limited to one tag inside the
innerHTML
property您可以根据需要添加任意多个标签,而不仅限于
innerHTML
属性中的一个标签
Now we add this newly defined element to window.customElements
:
现在,我们将此新定义的元素添加到window.customElements
:
window.customElements.define('custom-title', CustomTitle)
and we can use the <custom-title></custom-title>
Custom Element in the page!
我们可以在页面中使用<custom-title></custom-title>
自定义元素!
Note: you can’t use self-closing tags (in other words: this
<custom-title />
is not allowed by the standard)注意:您不能使用自动关闭标签(换句话说:该
<custom-title />
是标准不允许的)
Notice the -
dash in the tag name. We are required to use a dash in a Custom Element. This is how we can detect a built-in tag from a custom one.
注意标记名称中的-
破折号。 我们需要在自定义元素中使用破折号 。 这就是我们可以从自定义标签中检测内置标签的方式。
Now we have this element in the page, and we can do what we do with other tags: target it with CSS and JavaScript!
现在,我们在页面中有了这个元素,我们可以使用其他标签进行操作:使用CSS和JavaScript定位它!
提供元素的自定义CSS (Provide a custom CSS for the element)
In the constructor, you can pass a style
tag in addition to the HTML tag that defines the content, and inside that you can have the Custom Element CSS:
在构造函数中,除了可以定义内容HTML标记之外,还可以传递style
标记,并且在其中可以包含Custom Element CSS:
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<style>
h1 {
font-size: 7rem;
color: #000;
font-family: Helvetica;
text-align: center;
}
</style>
<h1>My Custom Title!</h1>
`
}
}
Here’s the example Custom Element we created, in Codepen:
这是我们在Codepen中创建的示例Custom Element:
See the Pen Web Components, My Custom Title by Flavio Copes (@flaviocopes) on CodePen.
见笔Web组件,我的自定义标题由弗拉维奥·科佩斯( @flaviocopes上) CodePen 。
语法较短 (A shorter syntax)
Instead of first defining the class and then calling window.customElements.define()
we can also use this shorthand syntax to define the class inline:
除了先定义类然后再调用window.customElements.define()
我们还可以使用以下速记语法来定义内联类:
window.customElements.define('custom-title', class extends HTMLElement {
constructor() {
...
}
})
添加JavaScript (Add JavaScript)
Just like we did for CSS, we can embed JavaScript.
就像我们对CSS所做的一样,我们可以嵌入JavaScript。
We can’t add it directly to the template tag like we did for CSS though.
但是,我们不能像我们对CSS那样直接将其添加到template标记中。
Here I define a click event listener in the Custom Element constructor:
在这里,我在Custom Element构造函数中定义了一个click事件监听器:
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `
<h1>My Custom Title!</h1>
`
this.addEventListener('click', e => {
alert('clicked!')
})
}
}
替代方案:使用模板 (Alternative: use templates)
Instead of defining the HTML and CSS in a JavaScript string, you can use a template
tag in HTML and assign it an id
:
除了在JavaScript字符串中定义HTML和CSS之外,您还可以在HTML中使用template
标记并为其分配一个id
:
<template id="custom-title-template">
<style>
h1 {
font-size: 7rem;
color: #000;
font-family: Helvetica;
text-align: center;
}
</style>
<h1>My Custom Title!</h1>
</template>
<custom-title></custom-title>
Then you can reference it in your Custom Element constructor and add it to the Shadow DOM:
然后,您可以在“自定义元素”构造函数中引用它,并将其添加到Shadow DOM:
class CustomTitle extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' })
const tmpl = document.querySelector('#custom-title-template')
this.shadowRoot.appendChild(tmpl.content.cloneNode(true))
}
}
window.customElements.define('custom-title', CustomTitle)
Example on Codepen:
Codepen上的示例:
See the Pen Web Components, My Custom Title with template by Flavio Copes (@flaviocopes) on CodePen.
请参阅CodePen上的Pen Web Components,“我的自定义标题”和 Flavio Copes( @flaviocopes )的模板 。
生命周期挂钩 (Lifecycle hooks)
In addition to constructor
, a Custom Element class can define those special methods that are executed at special times in the element lifecycle:
除constructor
,Custom Element类还可以定义在元素生命周期的特定时间执行的那些特殊方法:
connectedCallback
when the element is inserted into the DOM将元素插入DOM中的
connectedCallback
disconnectedCallback
when the element is removed from the DOM从DOM中删除元素时,
disconnectedCallback
的回调attributeChangedCallback
when an observed attribute changed, or is added or removedattributeChangedCallback
时观察到的属性改变,或者被添加或删除adoptedCallback
when the element has been moved to a new document元素移到新文档时采用了
adoptedCallback
class CustomTitle extends HTMLElement { constructor() { ... } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attrName, oldVal, newVal) { ... } }
attributeChangedCallback()
gets 3 parameters:
attributeChangedCallback()
获得3个参数:
- the attribute name 属性名称
- the old value of the attribute 属性的旧值
- the new value of the attribute. 属性的新值。
I mentioned it listens on observed attributes. What are those? We must define them in an array returned by the observedAttributes
static method:
我提到过它监听观察到的属性。 那些是什么? 我们必须在observedAttributes
静态方法返回的数组中定义它们:
class CustomTitle extends HTMLElement {
constructor() {
...
}
static get observedAttributes() {
return ['disabled']
}
attributeChangedCallback(attrName, oldVal, newVal) {
...
}
}
I defined the disabled
attribute to be observed. Now when it changes, for example in JavaScript we set disabled
to true:
我定义了要观察的disabled
属性。 现在,当它更改时,例如在JavaScript中,我们将disabled
设置为true:
document.querySelector('custom-title').disabled = true
the attributeChangedCallback()
fires with the set of parameters 'disabled', false, true
.
attributeChangedCallback()
会使用参数'disabled', false, true
的集合触发。
Note:
attributeChangedCallback()
can be called on the element using JavaScript (for some unknown - to me - reason), but you shouldn’t do that. It should just be invoked automatically by the browser注意:可以使用JavaScript在元素上调用
attributeChangedCallback()
(出于某种未知的原因-对我来说是原因),但是您不应该这样做。 它应该只是由浏览器自动调用
定义自定义属性 (Define custom attributes)
You can define custom attributes for your Custom Elements by adding a getter and setter for them:
您可以通过为它们的自定义元素添加一个getter和setter来定义自定义属性:
class CustomTitle extends HTMLElement {
static get observedAttributes() {
return ['mycoolattribute']
}
get mycoolattribute() {
return this.getAttribute('mycoolattribute')
}
set mycoolattribute(value) {
this.setAttribute('mycoolattribute', value)
}
}
This is how you can define boolean attributes, ones that are “true” if present, like disabled
for HTML elements:
这样可以定义布尔属性,如果存在,则为“ true”,例如disabled
HTML元素:
class CustomTitle extends HTMLElement {
static get observedAttributes() {
return ['booleanattribute']
}
get booleanattribute() {
return this.hasAttribute('booleanattribute')
}
set booleanattribute(value) {
if (value) {
this.setAttribute('booleanattribute', '')
} else {
this.removeAttribute('booleanattribute')
}
}
}
如何设置尚未定义的自定义元素的样式 (How to style a Custom Element that’s not yet defined)
JavaScript might take a little to kick in and a Custom Element might not be defined as soon as the page loads. The page might do an ugly re-layout when the element is added in the page.
JavaScript可能需要花费一点时间,并且页面加载后可能不会立即定义Custom Element。 在页面中添加元素时,页面可能会进行难看的重新布局。
To solve this problem, add a :not(:defined)
CSS pseudo class that sets the height and fades in the element when available:
要解决此问题,请添加:not(:defined)
CSS伪类,该伪类设置高度并在可用时淡入元素:
custom-title:not(:defined) {
display: block;
height: 400px;
opacity: 0;
transition: opacity 0.5s ease-in-out;
}
我可以在所有浏览器中使用它们吗? (Can I use them in all browsers?)
The current versions of Firefox, Safari and Chrome support them. IE will never do, and Edge at the time of writing has support for them in development.
当前版本的Firefox,Safari和Chrome支持它们。 IE将永远不会这样做,并且在撰写本文时,Edge在开发中已经为他们提供了支持。
You can use this polyfill to add a better support also for older browsers.
您可以使用此polyfill为旧版浏览器添加更好的支持。
翻译自: https://flaviocopes.com/web-components-custom-elements/
web找不到自定义组件方法