Custom labels
A custom tag defines a new HTML tag by extending an HTMLElement or a subclass of HTMLElement, which is componentized by native JS.
Custom tag through the window. The customElements. Define to define,
- The first parameter is the name of the tag, which must be followed by a dash
-
And it’s all lowercase - The second argument is the tag’s constructor, which is inherited from the tag mentioned above
HTMLElement
The class of - The third parameter accepts an object, which currently has only one
extends
Properties can be configured if the constructor is inherited fromHTMLElement
Subclasses of, such asHTMLDivElement
You need to specifyextends:"div"
After you define a custom element, you can use the custom element directly in HTML. If the custom element inherits from another element, you need to specify the name of the custom tag using the original tag plus the IS attribute
<! -- Inherit from HTMLElement -->
<ce-myelement></ce-myelement>
<! -- Inherited from the p tag -->
<p is="ce-my-p-element"></p>
Copy the code
Here is a simple example of clicking on an element that prints itself
class CopyCode extends HTMLElement {
constructor() {
super(a);this.onclick = (e) = > {
if(e.target ! =this) return;
console.log(e.target); }; }}window.customElements.define("ce-myelement", CopyCode);
Copy the code
The shadow of the DOM
create
The previous custom tags just define their own special generic methods, can also insert child elements, already have componentized methods, but compared to complex components are completely inadequate, it should be used in conjunction with another feature, Shadow DOM
Shadow DOM can seal the interior so that neither JS nor CSS can select the interior elements (they just can’t be selected and will still be displayed on the page), it can define
tags and only affect the interior styles
You can take over a normal element as a shadow DOM by using the following method
const innerNode = document.createElement("p");
innerNode.innerText = "inner";
const div = document.querySelector("div");
const shadow = div.attachShadow({ mode: "closed" });
shadow.appendChild(div.appendChild(innerNode));
Copy the code
When you set an element to shadow DOM, all of its children are hidden from the page, and elements in the Shadow DOM appear on the screen
The shadow DOM is available from the original element’s shadowRoot attribute, but not if the mode attribute is closed, which means that the element is completely closed and cannot be changed externally
const shadow = div.attachShadow({ mode: "closed" });
console.log(div.shadowRoot); // null
const shadow = div.attachShadow({ mode: "open" });
div.shadowRoot == shadow; // true
Copy the code
Add the style
Now that you’ve created a shadowDOM for the div, you can add elements and styles to it just like you would on a normal page
- By creating a
<style>
Label useinnerText
Hand writing - through
css
theimport url()
Method introduces an external style - through
<link>
Tags introduce external styles
slot
The shadow DOM takes over the internal content of the normal element, and the original content of the element is hidden. In this case, you can use the slot element
to introduce the external element into the shadow DOM and make it display in the appropriate place
As a simple example, change the text in div to the red H1 size text
const div = document.querySelector("div");
const shadow = div.attachShadow({ mode: "open" });
const h1 = document.createElement("h1");
h1.style.color = "red";
h1.appendChild(slot);
shadow.appendChild(h1);
Copy the code
Slots also support named slots, specifying the name by defining the name attribute on
, and specifying the slot with the same name on a normal element replaces the normal element with the shadow, and the default element can also be placed in
const div = document.querySelector("div");
const shadow = div.attachShadow({ mode: "open" });
const slot = document.createElement("slot");
slot.setAttribute("name"."h1");
const h1 = document.createElement("h1");
h1.style.color = "red";
h1.appendChild(slot);
shadow.appendChild(h1); // Add a slot element to the shadow
const text = document.createElement("div");
text.innerText = "h1";
text.setAttribute("slot"."h1");
div.appendChild(text); // Insert the element with the specified slot into the original element
Copy the code
The template
In the example above, we’ve been building the DOM tree with code. You can actually use the
tag to build the template. Unlike regular tags, the
tag doesn’t show up on the page, and it has the same CSS scope as the shadow DOM
Rewrite the above code as a template:
<div>aaa</div>
<template id="text">
<b slot="h1">text</b>
</template>
<template id="temp">
<style>
h1 {
color: red;
}
</style>
<h1>
<slot name="h1"></slot>
</h1>
</template>
<script src="./index.js" type="module"></script>
Copy the code
const div = document.querySelector("div");
const shadow = div.attachShadow({ mode: "open" });
shadow.appendChild(document.querySelector("#temp").content.cloneNode(true));
const text = document.querySelector("#text").content;
div.appendChild(text);
Copy the code
component
This, combined with the custom tags above, creates a component
<body>
<ce-red-h1 data-text="abc">
<b slot="h1">b</b>
</ce-red-h1>
<template id="temp">
<style>
h1 {
color: red;
}
</style>
<h1>
<slot name="h1"></slot>
</h1>
</template>
<script src="./index.js" type="module"></script>
</body>
Copy the code
class RedH1 extends HTMLElement {
text;
constructor() {
super(a);const template = document.querySelector("#temp");
this.attachShadow({ mode: "open" }).appendChild(template.content.cloneNode(true));
this.text = this.dataset.text;
const p = document.createElement("p");
p.innerText = this.text;
this.shadowRoot.appendChild(p); }}window.customElements.define("ce-red-h1", RedH1);
Copy the code
Custom tags can also pass data through data-, but only as strings
Used in the Vue
The document
Vue provides a defineCustomElement to create a custom tag constructor, it receives defineComponent the same parameters, return window will be used in the same class. The customElements. Define to register, Because it is registered using the native method, such components do not need to be mounted as global components to be used globally. Custom tags created using vUE templates can support passing complex data such as objects
To use custom labels in vUE, you must configure loader first; otherwise, a warning is displayed indicating that labels are not VUE components
// vite
vue({
template: {
compilerOptions: {
isCustomElement: (tag) = > tag.startsWith("ce-"), // Custom tags start with 'ce-' so they don't throw wrong}},})// vuecli
module.exports = {
chainWebpack: config= > {
config.module
.rule('vue')
.use('vue-loader')
.tap(options= > ({
...options,
compilerOptions: {
// Treat all tags beginning with ion- as custom elements
isCustomElement: tag= > tag.startsWith('ion-')}}))}}Copy the code
To prevent styles from being individually packaged externally during packaging, you need to change the vue file suffix to.ce.vue
Everything defined by the single-file component is put into the shadow DOM of the custom element
<template> <h1 class="redH1"> <slot name="h1"></slot> </h1> <p>{{ text.value }}</p> </template> <script setup lang="ts"> interface Props { text: { value? : string; }; } withDefaults(defineProps<Props>(), { text: () => ({ value: "a", }), }); </script> <style> // scoped h1 {color: red; } </style>Copy the code
// index.ts
import { defineCustomElement } from "vue";
import RedH1 from "./RedH1.ce.vue";
window.customElements.define("ce-red-h1", defineCustomElement(RedH1));
Copy the code
Then you can use index globally by introducing it as a side effect in main
<template>
<ce-red-h1 .text="text">
<b slot="h1">b</b>
</ce-red-h1>
</template>
<script setup lang="ts">
import { reactive } from "@vue/reactivity";
const text = reactive({
value: "abc",
});
</script>
Copy the code
Note that if you’re passing objects, arrays, etc., instead of using V-bind :text, use V-bind :text.prop. Using a single file will pack more code into it, and native writing is recommended if you are using simple functional components
Usage scenarios
If you need to extend the HTML obtained from outside and add more complex functions, custom tags are a good choice. For example, my blog post is parsed to HTML through Markdown. Just copy the button in the upper right corner of the code snippet of the parsed HTML text is a custom tag. By customizing the click event to copy the innerText from the parent element directly into the clipboard, you don’t have to set the paste content for each snippet as you do for the Paste button