Web Component 简单示例

前言

Web Component: 创建可重用的定制元素

学习内容来源:

调试设置:

谷歌浏览器:设置------preferences------Elements------Show user agent shadow DOM打钩

基本概念

  • Custom element(自定义元素):class或者function,定义组件api。
  • Shadow DOM(影子 DOM):附加到元素中,可隔离样式。
  • HTML template(HTML 模板): <template> 和 <slot> 可以编写不在呈现页面中显示的标记模板。

自定义组件

  1. 注册组件
js 复制代码
// TodoItem.js
class TodoItem extends HTMLElement{}
customElements.define("todo-item", TodoItem)  // 传入组件名称与组件
html 复制代码
<!--index.html-->
<!--使用-->
<todo-item></todo-item>
  1. 创建模板
js 复制代码
// TodoItem.js
const template=document.createElement("template")
template.innerHTML=`
    <style>
        label{
            display: block;
        }
        .description{
            color: #a9a9a9;
            font-size: .8em;
        }
    </style>

    <label>
        <input type="checkbox" />
        <slot></slot>
        <span class="description">
            <slot name="description"></slot>
        </span>
    </label>
`

也可以直接写在html中

html 复制代码
<!--index.html-->
<template id="myTemp">
    <style>
        label{
            display: block;
        }
        .description{
            color: #a9a9a9;
            font-size: .8em;
        }
    </style>

    <label>
        <input type="checkbox" />
        <slot></slot>
        <span class="description">
            <slot name="description"></slot>
        </span>
    </label>
<template>
  1. 开启shadow dom
js 复制代码
// TodoItem.js
class TodoItem extends HTMLElement{
    constructor(){
        super()
        // ++ 开启shadow dom
        const shadowDom = this.attachShadow({ mode : "open"})
        // ++ 如果模板是写在js中,直接克隆模板,并把模板加入到shadow dom中:
        shadowDom.appendChild(template.content.cloneNode(true))
        
        // 否则需要获取一下模板: 
        // let template = ducoment.querySelector('#myTemp')
        // shadowDom.appendChild(template.content.cloneNode(true))
    }
}

现在可以在插槽中加入一些内容。在模板中设置的<style>将不会影响到外边的元素:

html 复制代码
<todo-item>
    <!--内容会加到默认slot中 -->
    todo1    
    
    <!--内容会加到description slot中 -->
    <span slot="description">其他描述</span> 
</todo-item>
  1. 生命周期
js 复制代码
// TodoItem.js

class TodoItem extends HTMLElement{
    constructor(){
        super()
        const shadowDom = this.attachShadow({ mode : "open"})
        shadowDom.appendChild(template.content.cloneNode(true))
    }
    
	// ++ 自定义元素挂载时被调用
	connectedCallback() {}
	// ++ 自定义元素卸载时被调用
	disconnectedCallback() {}
}

内置元素扩展与组件属性

  1. 注册组件:继承ul标签
js 复制代码
// ExpandableList.js
class ExpandableList extends HTMLUListElement {}

customElements.define('expandable-list', ExpandableList, {
   extends: 'ul',
});
  1. 定义组件
js 复制代码
class ExpandableList extends HTMLUListElement {
   constructor() {
      super();
      this.style.position = 'relative';

	  // 创建一个子元素
      this.toggleBtn = document.createElement('button');
      this.toggleBtn.style.position = 'absolute';
      this.toggleBtn.style.border = 'none';
      this.toggleBtn.style.background = 'none';
      this.toggleBtn.style.padding = 0;
      this.toggleBtn.style.top = 0;
      this.toggleBtn.style.left = '5px';
      this.toggleBtn.style.cursor = 'pointer';
      this.toggleBtn.innerText = '>';
      this.appendChild(this.toggleBtn);
      
	 // 定义点击事件
      this.toggleBtn.addEventListener('click', () => {
         this.dataset.expanded = !this.isExpanded;
      });

      // 获取某个属性
      console.log(this.getAttr('name'))
   }

   // 获取属性
    private getAttr (key) {
		return this.getAttribute(key)
	}

   // 计算属性
   get isExpanded() {
      return (
         this.dataset.expanded !== 'false' && this.dataset.expanded !== null
      );
   }

   // 监听属性
   static get observedAttributes() {
      return ['data-expanded'];
   }

  // 监听到上方属性变化后调用
   attributeChangedCallback(name, oldValue, newValue) {
      this.updateStyles();
   }


   updateStyles() {
      const transform = this.isExpanded ? 'rotate(90deg)' : '';
      this.toggleBtn.style.transform = transform;
      [...this.children].forEach((child) => {
         if (child !== this.toggleBtn) {
            child.style.display = this.isExpanded ? '' : 'none';
         }
      });
   }
   
   // 挂载后调用
   connectedCallback() {
      this.updateStyles();
   }
}

customElements.define('expandable-list', ExpandableList, {
   extends: 'ul',
});
  1. 使用:通过is传入扩展
html 复制代码
<ul is="expandable-list" data-expanded name="myul">
    <li>apple</li>
    <li>banana</li>
</ul>
相关推荐
zttbee10 分钟前
echarts图表tooltip色块失效
前端·javascript·echarts
excel2 小时前
IndexNow:让搜索引擎更快发现网站更新的主动通知协议
前端·后端·架构
子兮曰2 小时前
CSS 复现 23 种设计风格:从拟物到液态玻璃的成本阶梯与兜底写法【实战】
前端·css·视觉设计
虚无境2 小时前
如何用AI帮我写出实用的插件
前端·javascript·ai编程
柳杉3 小时前
从数字化到数智化:3D 可视化大屏的下一个五年
前端·ai编程·数据可视化
风骏时光牛马4 小时前
从零到实战:AI全栈学习进阶路线
前端
FRing034 小时前
JavaScript入门到精通二十六(变量提升、函数剩余参数、展开运算符、ES6箭头函数)
开发语言·前端·javascript·es6·箭头函数
用户360055579004 小时前
Kestrel_VLLM - 15 · decode 优化四连击:19ms → 8ms
前端
IT_陈寒4 小时前
JavaScript的闭包陷阱,这个坑我躺了三天才爬出来
前端·人工智能·后端
计算机魔术师5 小时前
Microsoft Research 与合作者证明 31 年历史的 KLS 猜想
前端