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>
相关推荐
sasaraku.9 分钟前
serviceWorker缓存资源
前端
RadiumAg1 小时前
记一道有趣的面试题
前端·javascript
yangzhi_emo1 小时前
ES6笔记2
开发语言·前端·javascript
yanlele2 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子3 小时前
React状态管理最佳实践
前端
烛阴3 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子3 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...3 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
天天扭码4 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html
xw54 小时前
我犯了错,我于是为我的uni-app项目引入环境标志
前端·uni-app