web component - 使用HTML Templates和Shadow DOM构建现代UI组件

news2024/9/24 1:24:38

Web Component是一种用于构建可重用的UI组件的技术。它使用标准化的浏览器API,包括Custom Elements、Shadow DOM和HTML Templates来实现组件化开发方式。这些API都是现代浏览器原生支持的,因此不需要引入第三方库或框架即可使用。

在这篇博客中,我们将介绍如何使用Web Component技术构建一个名为UserCard的UI组件。这个组件可以显示用户的头像、姓名和电子邮件地址,并提供一个关注按钮。当用户点击关注按钮时,组件会触发一个自定义事件,允许开发者处理按钮点击事件。

在线例子和源码点击我

首先,让我们来看看这个组件的代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 使用template写模板 -->
  <template id="card-template">
    <div class="user-card-container">
      <img />
      <div class="content">
        <div class="name"></div>
        <div class="email"></div>
        <button class="follow-btn"></button>
      </div>
    </div>
  </template>

  <user-card
    id="user-card-id"
    userId="1"
    image="https://semantic-ui.com/images/avatar2/large/kristy.png"
    name="User Name"
    email="yourMail@some-email.com"
    hasFollow="false"
    handleFollowEvent="handleFollowEvent"
  ></user-card>

  <script src="./user-card.js"></script>
  <script>
    const userCard = document.getElementById('user-card-id')

    /** 监听组件事件 */
    userCard.addEventListener('follow-event', (e) => {
      const functionName = e.detail.method
      this[functionName] && this[functionName](e.detail.data)
    })

    /** 修改按钮状态  */
    function handleFollowEvent(data) {
      userCard.setAttribute('hasFollow', userCard.getAttribute('hasFollow') === 'true' ? 'false' : 'true')
    }
  </script>
</body>
</html>
class UserCard extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: "open" });
  }

  /** 内容挂载时候回调 */
  connectedCallback() {
    this.render();
  }

  /** 指定监听的属性列表 */
  static get observedAttributes() {
    return ["hasfollow"];
  }

  /** 监听属性变化 */
  attributeChangedCallback(name, newValue, oldValue) {
    // 加上newValue判断是防止第一次传属性就执行了
    if (newValue && newValue !== oldValue) {
      this.processDomRender();
    }
  }

  /** 具体的渲染函数 */
  render() {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = "./user-card.css";
    // css加载完才开始渲染内容,避免页面错乱问题
    link.onload = () => {
      // 把模板内容挂在上shadow dom
      this.processDomRender();
    };
    this.shadowRoot.appendChild(link);
  }

  /** 仅render dom */
  processDomRender() {
    const userCardContainer = this.shadowRoot.querySelector(
      ".user-card-container"
    );
    userCardContainer && this.shadowRoot.removeChild(userCardContainer);
    this.shadowRoot.appendChild(this.getContent());
    this.setContent();
  }

  /** 获取模板 */
  getContent() {
    const template = document.getElementById("card-template");
    // 拷贝模板,但是不改动模板的内容
    const content = template.content.cloneNode(true);
    return content;
  }

  /** 把组件传下来的属性挂载到shadow dom上 */
  setContent() {
    this.shadowRoot
      .querySelector("img")
      .setAttribute("src", this.getAttribute("image"));
    this.shadowRoot.querySelector(".name").innerHTML =
      this.getAttribute("name");
    this.shadowRoot.querySelector(".email").innerHTML =
      this.getAttribute("email");
    this.shadowRoot.querySelector(".follow-btn").innerHTML =
      this.getAttribute("hasFollow") === "true" ? "已关注" : " 关注";
    // 按钮点击事件
    this.shadowRoot
      .querySelector(".follow-btn")
      .addEventListener("click", () => {
        const event = new CustomEvent("follow-event", {
          detail: {
            method: this.getAttribute("handleFollowEvent"),
            data: {
              id: this.getAttribute("userId"),
            },
          },
        });
        this.dispatchEvent(event);
      });
  }
}

customElements.define("user-card", UserCard);


.user-card-container {
  height: 120px;
  display: flex;
  background-color: #d4d4d4;
  border: 1px solid #d5d5d5;
  box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
  border-radius: 3px;
  overflow: hidden;
  padding: 10px;
  box-sizing: border-box;
}

.user-card-container img {
  height: 100px;
  width: 100px;
}

.user-card-container .content {
  margin: 10px;
  flex: 1;
  display: flex;
  flex-direction: column;
}

.user-card-container .content .name {
  font-size: 20px;
  font-weight: 600;
  line-height: 1;
  margin: 0;
  margin-bottom: 5px;
}
.user-card-container .content .email {
  padding: 5px 0;
  font-size: 12px;
  opacity: 0.75;
  line-height: 1;
  margin: 0;
  margin-bottom: 15px;
}
.user-card-container .content .follow-btn {
  width: 100px;
  font-size: 12px;
  border-radius: 5px;
  text-transform: uppercase;
}

这个组件使用了三个Web Component的API:Custom Elements、Shadow DOM和HTML Templates。Custom Elements用于定义一个自定义元素UserCard,Shadow DOM用于封装组件内部的样式和结构,防止外部样式干扰组件,HTML Templates则用于定义组件的模板。

在构造函数中,我们使用attachShadow方法创建了一个shadowRoot,并将其挂载到自定义元素上。接着,在connectedCallback方法中,我们调用了render方法来渲染组件。

在render方法中,我们首先将组件的CSS样式引入到shadowRoot中。接着,我们调用processDomRender方法来渲染组件的结构和内容。

在processDomRender方法中,我们首先使用querySelector方法查找user-card-container元素是否已经存在,如果存在,则将其从shadowRoot中删除。接着,我们调用getContent方法获取组件的模板,并将其添加到shadowRoot中。最后,我们调用setContent方法将组件的属性挂载到shadowRoot中。

在getContent方法中,我们使用querySelector方法获取id为card-template的模板,并使用cloneNode方法创建一个模板副本。由于模板是HTML5的新特性,因此我们需要为HTML文件添加声明来确保浏览器正确解析模板。

在setContent方法中,我们使用getAttribute方法获取组件的属性,并将其挂载到shadowRoot中。我们还为关注按钮添加了一个点击事件,并在事件处理程序中触发了一个自定义事件。

最后,在JavaScript代码中,我们使用addEventListener方法监听组件的follow-event事件,并在事件处理程序中调用handleFollowEvent方法来处理按钮点击事件。当用户点击关注按钮时,handleFollowEvent方法将切换hasFollow属性,并重新渲染组件。
效果图如下
在这里插入图片描述
在这里插入图片描述

这是一个简单的Web Component示例,它介绍了如何使用Custom Elements、Shadow DOM和HTML Templates API来构建可重用的UI组件。通过使用这些API,我们可以将组件内部的样式和结构封装起来,防止外部样式干扰组件,并使开发者能够轻松地重复使用和维护组件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1353339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何学习TS?

文章目录 一. 8种内置基础类型.ts二. void、never、any、unknown类型void类型never类型any类型unknown类型总结&#xff1a;void和any在项目中是比较常见的&#xff0c;never和unknown不常用。 三. 数组和函数类型定义.ts四. 元组与交叉类型使用元组数组一般有同类型的值组成&a…

页面间动画之放大缩小视图

目录 1、Exchange类型的共享元素转场 2、Static类型的共享元素转场 3、场景示例 在不同页面间&#xff0c;有使用相同的元素&#xff08;例如同一幅图&#xff09;的场景&#xff0c;可以使用共享元素转场动画衔接。为了突出不同页面间相同元素的关联性&#xff0c;可为它们…

管理文件传输的工具CopyQueue mac功能特点

CopyQueue for mac是用于管理文件传输的工具&#xff0c;可以有效地复制多个文件&#xff0c;并且比OS X的速度更快&#xff0c;暂停和恢复文件传输&#xff0c;日程任务和更多&#xff0c;使用和管理起来都非常的简单方便。如果你要复制很多文件&#xff0c;CopyQueue更快更高…

pytest conftest定义一个fixtrue获取测试环境地址

方便全局切换地址 pytest.fixture() def config():data {测试环境: {A环境: 127.0.0.1,B环境: 127.0.0.2,C环境: 127.0.0.3,D环境: 127.0.0.4},}return data.get(测试环境, {}).get(A环境)import pytestdef test_case001(config):url http://str(config):8080/api/user/logi…

从零开始了解大数据(七):总结

系列文章目录 从零开始了解大数据(一)&#xff1a;数据分析入门篇-CSDN博客 从零开始了解大数据(二)&#xff1a;Hadoop篇-CSDN博客 从零开始了解大数据(三)&#xff1a;HDFS分布式文件系统篇-CSDN博客 从零开始了解大数据(四)&#xff1a;MapReduce篇-CSDN博客 从零开始了解大…

基于rk3568 Android H265推流SRS低延迟网页播放方案

在音视频领域&#xff0c;融合推流&#xff0c;低码流&#xff0c;低延迟&#xff0c;浏览器H5化是一个降低成本&#xff0c;提升用户体验的重要手段。同时适配现有直播的生态也是一个必要条件。 在满足上述要求的情况下&#xff0c;我做了以下实践&#xff0c;取得了良好的效果…

每天五分钟计算机视觉:揭秘迁移学习

本文重点 随着人工智能的迅速发展,深度学习已经成为了许多领域的关键技术。然而,深度学习模型的训练需要大量的标注数据,这在很多情况下是不现实的。迁移学习作为一种有效的方法,可以在已有的数据和模型上进行训练,然后将其应用于新的任务。这种方法大大降低了对新任务的…

opencv期末练习题(2)附带解析

图像插值与缩放 %matplotlib inline import cv2 import matplotlib.pyplot as plt def imshow(img,grayFalse,bgr_modeFalse):if gray:img cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)plt.imshow(img,cmap"gray")else:if not bgr_mode:img cv2.cvtColor(img,cv2.COLOR_B…

2023机器人行业总结,2024机器人崛起元年(具身智能)

2023总结&#xff1a; 1.Chatgpt引爆了通用人工智能&#xff0c;最大的受益者或是机器人&#xff0c;2023年最热门的创业赛道便是人形机器人&#xff0c;优必选更是成为人形机器人上市第一股&#xff0c; 可以说2023年是机器人开启智能化的元年&#xff0c;而2024则将成为机器…

Java网络编程之IP,端口号,通信协议(UDP,TCP)

目录 1.软件架构2.网络编程三要素3.IP1.IPV42.IPV6 4.端口号5.协议1.UDP协议1.单播2.组播3.广播 2.TCP协议1.三次握手2.四次挥手 1.软件架构 ①C/S&#xff1a;客户端/服务器 在用户本地需要下载安装客户端程序&#xff0c;在远程有一个服务器端程序。 优点&#xff1a;画面精美…

三千多个厂商默认帐号、默认密码(建议收藏)

晚上好&#xff0c;我的网工朋友。 做网工这行&#xff0c;多少都会遇上各种各样的厂商设备&#xff0c;遇上一些新设备&#xff0c;虽然没有更改密码&#xff0c;但不知道初始默认账号和密码是啥。 今天就给你整理了一波&#xff0c;三千多个厂商默认帐号、默认密码&#xf…

Java学习,一文掌握Java之SpringBoot框架学习文集(2)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

LLVM(简介)

历史 LLVM(low level virtual machine)起源于伊利诺伊大学的一个编译器实验项目&#xff0c;目前已经发展成一个集编译器和工具链为一体的商业开源项目&#xff0c;因此其英文名称的含义被扩大&#xff0c;不再仅仅是字面意思。其创始人为 Chris Lattner。LLVM项目遵循的开源许…

流媒体学习之路(WebRTC)——Pacer与GCC(5)

流媒体学习之路(WebRTC)——Pacer与GCC&#xff08;5&#xff09; —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全…

单片机外设矩阵键盘之线反转法识别原理与示例

单片机外设矩阵键盘之线反转法识别原理与示例 1.概述 这篇文章主要介绍单片机接收 4X4矩阵键盘发出的指令&#xff0c;做出对应的反馈。其中主要介绍矩阵键盘线反转方式的识别原理和实操。 2.矩阵键盘线反转识别原理 2.1.矩阵键盘硬件接线原理 矩阵键盘的硬件接线方式有多种…

MySQL第三战:CRUD,函数1以及unionunion all

前言 在当今的数字化时代&#xff0c;数据库已经成为信息管理的重要工具。其中&#xff0c;MySQL作为一种流行的关系型数据库管理系统&#xff0c;已经广泛应用于各种业务场景。在本文中&#xff0c;我们将深入探讨MySQL中的核心概念&#xff0c;包括创建&#xff08;Create&a…

感恩客户相伴23载,泛微2024持续向上!

2023年&#xff0c;国家大力推动数字经济发展&#xff0c;各行各业在加速数字化转型&#xff0c;在这一年&#xff0c;泛微保持持续增长&#xff0c;引领行业发展&#xff0c;为组织的数字化转型助力。感恩客户与伙伴朋友的支持与信任&#xff01; 01.泛微中大客户总量突破8万余…

burpsuite模块介绍之extender(扩展)

extender Burp提供了对第三方拓展插件的支持,使用户能够编写自定义插件或从插件商店中安装拓展插件。这些Burp扩展程序可以以多种方式定制Burp的行为,包括修改HTTP请求和响应、自定义UI、添加自定义扫描程序检查以及访问关键的运行时信息,如代理历史记录、目标站点地图和扫…

Ubuntu Server 22.04 连接Wifi并配置静态IP

Ubuntu Server 22.04 连接Wifi并配置静态IP 前言&#xff1a;我家最近好几台电脑&#xff0c;我都想跑着Ubuntu Server做服务器&#xff0c;但是近几年的超级本已经不自带网口了&#xff0c;所以我就考虑用Wifi来联网&#xff0c;速度也还可以&#xff0c;但是既然是跑服务&…

工作中redis相关知识总结

这里写目录标题 一、Redis数据持久化概念二、redis数据类型三、redis缓存的应用流程四、什么样的数据适合存放到redis中&#xff1f;1、什么情况下&#xff0c;redis中会没有数据&#xff1f;2、redis缓存项目在测试中的注意事项a、更新缓存b、淘汰缓存 五、什么是缓存击穿1、缓…