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>
相关推荐
小彭努力中25 分钟前
9.Three.js中 ArrayCamera 多视角相机详解+示例代码
开发语言·前端·javascript·vue.js·数码相机·ecmascript·webgl
朝阳3934 分钟前
Electron Forge【实战】桌面应用 —— 将项目配置保存到本地
前端·javascript·electron
若愚67921 小时前
Tauri 跨平台开发指南及实战:用前端技术征服桌面应用(合集-万字长文)
前端·vue.js·rust·gitcode
不秃的开发媛1 小时前
前端技术Ajax实战
前端·javascript·ajax
IT瘾君1 小时前
JavaWeb:后端web基础(Tomcat&Servlet&HTTP)
前端·servlet·tomcat
qq_419429132 小时前
uni-app中使用RenderJs 使用原生js
前端·javascript·uni-app
沃野_juededa2 小时前
uniapp自定义选项卡
java·前端·javascript
刺客-Andy2 小时前
React 第三十五节 Router 中useNavigate 的作用及用途详解
前端·javascript·react.js·前端框架
soragui2 小时前
【React】轻松掌握 React 中 useEffect的使用
前端·react.js·前端框架
沙尘暴炒饭2 小时前
解决vue3 路由query传参刷新后数据丢失的问题
开发语言·前端·javascript