WebComponent
Advantages: Native components, no framework required, good performance and less code Disadvantages: compatibility issues
Componentization benefits: high cohesion, reuse, composability
The core technology
custom elements
: a set ofJavaScript API
, allow definitioncustom elements
And its behavior, which can then be used as needed in the user interfaceShadow DOM
: a set ofJavaScript API
, used to encapsulateDOM tree attached to the element (rendered separately from the main document DOM)
And control its associated functions. This way, you can keep the functionality of an element private, and it can be scripted and styled without worrying about running afoul of the rest of the documentHTML templates
:<template>
andslot
Element allows the user to write markup templates that are not displayed on rendered pages. They can then be reused many times as a basis for defining the structure of the element
Implement custom Button components
HTML template template
The content in template is defined as a Button component style, and slot takes the content from a custom component and inserts it into the template
<comp-button type='primary'>I'm a button</comp-button>
<template id="btn">
<button class="comp-btn">
<slot>I'm the default</slot>
</button>
</template>
Copy the code
Component implementation
class CompButton extends HTMLElement {
constructor() {
super(a);// Create a shadow, where this is comp-button
const shadow = this.attachShadow({
mode: 'open'
});
// Find the template
const temp = document.getElementById('btn');
// Clone the template to ensure the reuse of the template
const cloneTemp = temp.content.cloneNode(true);
// Set some properties
const style = document.createElement('style');
const type = this.getAttribute('type') | |'default'
const styleList = {
'primary': {
background: '#409eff'.color: '#fff'
},
'default': {
background: '# 909399'.color: '#fff'
}
}
style.textContent = `
.comp-btn{
outline:none;
border:none;
border-radius:4px;
padding:5px 10px;
display:inline-flex;
background:var(--background-color,${styleList[type].background});
color:var(--text-color,${styleList[type].color});
cursor:pointer
}
`
// Add style to shadow
shadow.appendChild(style);
// Template is mounted to shadowshadow.appendChild(cloneTemp); }}Copy the code
Define custom components
// Define a custom component
window.customElements.define('comp-button', CompButton);
Copy the code
WebComponent life cycle
connectedCallback
: called when a Custom Element is first inserted into the document DOMdisconnectedCallback
: is called when a Custom Element is removed from the document DOMadoptedCallback
: Called when a custom Element is moved to a new document (move into an iframe)attributeChangedCallback
: Called when a Custom Element is added or removed, or its attributes are modified
Implementation similar to Collapse Collapse panel
├─ index.html ├─ index.js ├── ly collapse.js ├─ ly collapse.js
Results show
index.html
<! -- * @Author: dfh * @Date: 2021-02-02 07:58:07 * @LastEditors: dfh * @LastEditTime: 2021-02-02 09:02:54 * @Modified By: dfh * @FilePath: /day15-webcomponent/2.index.html -->
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<ly-collapse>
<ly-collapse-item title="Consistency" name="1">
<div>Consistent with real life: consistent with the flow and logic of real life, follow the language and concepts used by users;</div>
<div>Consistency in the interface: All elements and structures must be consistent, such as design style, ICONS and text, element placement, etc.</div>
</ly-collapse-item>
<ly-collapse-item title="Feedback Feedback" name="2">
<div>Control feedback: users can clearly perceive their own operations through interface styles and interactive effects;</div>
<div>Page feedback: After operation, the current state is clearly displayed through changes in page elements.</div>
</ly-collapse-item>
<ly-collapse-item title="Efficiency Efficiency" name="3">
<div>Simplified process: design simple and intuitive operation process;</div>
<div>Clear and explicit: clear language expression and clear meaning, so that users can quickly understand and make decisions;</div>
<div>Help users identify: simple and straightforward interface, let users quickly identify rather than recall, reduce user memory burden.</div>
</ly-collapse-item>
<ly-collapse-item title="Controllable Controllability" name="4">
<div>User decision-making: Operation suggestions or safety tips can be provided to users according to scenarios, but decisions cannot be made on behalf of users.</div>
<div>Controllable result: Users can perform operations freely, including undo, rollback, and termination of the current operation.</div>
</ly-collapse-item>
</ly-collapse>
<! -- Template has no real meaning and will not be rendered to the page -->
<template id='collapse-tmpl'>
<div class="collapse">
<slot></slot>
</div>
</template>
<template id='collapse-item-tmpl'>
<div class='title'></div>
<div class='content'>
<slot></slot>
</div>
</template>
<! -- type='module' -->
<script src="./index.js" type='module'></script>
</body>
</html>
Copy the code
index.js
/* * @Author: dfh * @Date: 2021-02-02 07:59:52 * @LastEditors: dfh * @LastEditTime: 2021-02-02 10:19:58 * @Modified By: dfh * @FilePath: /day15-webcomponent/index.js */
import LyCollapse from './ly-collapse.js';
import LyCollapseItem from './ly-collapse-item.js';
window.customElements.define('ly-collapse', LyCollapse);
window.customElements.define('ly-collapse-item', LyCollapseItem);
// Set the default state: 1 display, other hidden
const defaultActive = ['1'];
// Get collapse elements
const collapseEl = document.querySelector('ly-collapse');
// Pass data to the child
collapseEl.setAttribute('active'.JSON.stringify(defaultActive));
// Listen for custom events
collapseEl.addEventListener('changeName'.e= > {
const {
isShow,
name
} = e.detail;
if (isShow) {
const index = defaultActive.indexOf(name);
/ / remove
defaultActive.splice(index, 1);
} else {
/ / add
defaultActive.push(name);
}
collapseEl.setAttribute('active'.JSON.stringify(defaultActive));
});
Copy the code
ly-collapse.js
/* * @Author: dfh * @Date: 2021-02-02 07:59:33 * @LastEditors: dfh * @LastEditTime: 2021-02-02 10:01:01 * @Modified By: dfh * @FilePath: /day15-webcomponent/ly-collapse.js */
export default class LyCollapse extends HTMLElement {
constructor() {
super(a);// Create a shadow node
const shadow = this.attachShadow({
mode: 'open'
});
// Find the template
const tmpl = document.getElementById('collapse-tmpl');
// Clone template,true means clone includes descendant nodes
const tmplClone = tmpl.content.cloneNode(true);
/ / style
let style = document.createElement('style');
//:host represents the shadow root element
style.textContent = ` :host{ display:flex; border:3px solid #ebebeb; border-radius:5px; width:100%; } .collapse{ padding:10px; width:100%; } `
// Associate the template root shadow
shadow.appendChild(tmplClone);
// Add styles
shadow.appendChild(style);
// Monitor slot changes
const slot = shadow.querySelector('slot');
slot.addEventListener('slotchange'.(e) = > {
this.slotList = e.target.assignedElements();
this.render(); })}// Monitor property changes
static get observedAttributes() {
return ['active']}attributeChangedCallback(key, oldVal, newVal) {
if (key === 'active') { // Accept active change processing
this.activeList = newVal;
this.render(); }}render() {
//activeList has a value and the child is loaded
if (this.activeList && this.slotList) {
this.slotList.forEach(child= > {
child.setAttribute('active'.this.activeList); }); }}}Copy the code
ly-collapse-item.js
/* * @Author: dfh * @Date: 2021-02-02 07:59:46 * @LastEditors: dfh * @LastEditTime: 2021-02-02 10:24:42 * @Modified By: dfh * @FilePath: /day15-webcomponent/ly-collapse-item.js */
export default class LyCollapseItem extends HTMLElement {
constructor() {
super(a);const shadow = this.attachShadow({
mode: 'open'
});
const tmpl = document.getElementById('collapse-item-tmpl');
const tmplClone = tmpl.content.cloneNode(true);
this.isShow = false; // Indicates whether to display
const style = document.createElement('style');
style.textContent = ` :host{ width:100%; border-bottom:1px solid #ebeef5; display:flex; flex-direction:column; padding:10px 0; color:black; } .title{ background:#f1f1f1; line-height:35px; font-size:18px; } .content{ font-size:14px; } `
shadow.appendChild(style);
shadow.appendChild(tmplClone);
this.titleEl = shadow.querySelector('.title');
// Set the click event for the title
this.titleEl.addEventListener('click'.() = > {
//dispatchEvent dispatches a custom event
document.querySelector('ly-collapse').dispatchEvent(new CustomEvent('changeName', {
detail: {
name: this.getAttribute('name'),
isShow: this.isShow
}
}))
})
}
// Monitor property changes
static get observedAttributes() {
return ['active'.'title'.'name']}attributeChangedCallback(key, oldVal, newVal) {
switch (key) {
case 'title': // Accept the title attribute
this.titleEl.innerHTML = newVal;
break;
case 'active':
this.activeList = JSON.parse(newVal); // The value passed by the parent component
break;
case 'name':
this.name = newVal;
break;
default:
break;
}
if (this.name && this.activeList) {
// Whether the active number contains name
this.isShow = this.activeList.includes(this.name);
//this.shadowRoot gets the shadow tree
this.shadowRoot.querySelector('.content').style.display = this.isShow ? 'block' : 'none'; }}}Copy the code