Web Components之自定义HTML标签

一、Web Components

  Web Components是一种组件化的方式,它允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。Web Components的一个重要特性是封装,通过Web Components创建的组件,可以有效地将其自身的html结构、css样式及行为隐藏起来,并从页面中的其他代码分离出来,也就是每个组件都是独立的,不会互相干扰。下面是一个Web Components的简单应用:

<custom-button text="自定义按钮"></custom-button>

在这里插入图片描述
  从上面可以看出我们通过自定义了一个custom-button标签,让按钮拥有了一些自定义的样式,可以看出这种方式其实就是组件化的思想。那么这样一个自定义标签如何实现的呢?

二、自定义标签

  W3C制定了两种custom elements: Autonomous custom elementsCustomized built-in elements

2.1 Autonomous custom elements

  独立的元素,这种元素不会继承现有内建的HTML标签。你可以通过<custom-button></custom-button>document.createElement('custom-button')来进行使用。
  下面我们来实现上面的小例子,实现这样一个例子很简单,W3C提供了一个HTMLElement类,我们通过继承该类即可实现我们自定义的标签:

class CustomButton extends HTMLElement{
  constructor() {
    super()
    
    // 创建一个shadow root
    const shadow = this.attachShadow({mode: 'open'})
    
    // 创建一个button
    const button = document.createElement('button')
    button.setAttribute('class', 'custom-button')
    
    const text = this.getAttribute('text')
    button.textContent = text
    
    // 创建样式
    const style = document.createElement('style')
    style.textContent = `
      .custom-button{
        border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
      }
    `
    
    // 将创建的元素添加到shadow dom中
    shadow.appendChild(style)
    shadow.appendChild(button)
  }
  
}

// 注册自定义的标签
customElements.define('custom-button', CustomButton)

2.1.1 Shadow DOM

  可以看出上面代码在继承HTMLElement之后,我么执行了this.attachShadow({mode: 'open'}),该行是创建一个Shadow Dom,那么Shadow Dom是个什么东西呢?Shadow Dom是每个标签之间不会互相干扰的关键所在,它可以将一个隐藏的、独立的DOM添加到一个元素上。操作Shadow Dom与常规的Dom没有任何区别——例如添加子节点、设置属性,以及为节点添加自己的样式,或者为整个 Shadow DOM添加样式(例如在<style> 元素内添加样式)。不同的是,Shadow DOM内部的元素始终不会影响到它外部的元素,这为封装提供了便利。

  • Shadow DOM 基本用法
      可以使用Element.attachShadow()方法来创建一个Shadow Dom,它可以被添加到任何一个元素中。它接受一个配置对象作为参数,该对象有一个mode属性,值可以是open或者closed
let shadow = this.attachShadow({mode: 'open'});
let shadow = this.attachShadow({mode: 'closed'});

  open表示你可以通过页面内的 JavaScript 方法来获取Shadow DOM,例如上面例子:

let shadow = document.querySelector('custom-button').shadowRoot
console.log(shadow) // 此处shadow就是shadow dom下的所有子节点;如果为‘closed’,则为null

2.2 Customized built-in elements

  该类型元素是用过继承html中内建的元素进行自定义。例:

class CustomButton2 extends HTMLButtonElement{
  constructor () {
    super()
    this.style = `
    border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
    `
  }
}

customElements.define('custom-button2', CustomButton2, {extends: 'button'})

  注意这里继承的不再是HTMLElement,而是HTMLButtonElement,当前你也可以继承HTMLSpanElementHTMLInputElement等,注册时需要添加第三个参数,该参数指明我们需要继承的元素。在使用时,通过<button is="custom-button2">ddd</button>document.createElement('button', {is: 'custom-button2'})

三、<template>与<slot>

3.1 <template>

  当网页上重复使用相同的标记结构时,使用某种模板而不是一遍又一遍地重复相同的结构是有意义的。以前这是可行的,但HTML<template> 元素使它更容易实现。 此元素及其内容不会在DOM中呈现,但仍可使用JavaScript去引用它。

<template id="template">
  <p>template</p>
</template>
const template = document.querySelector('#template')
const content = template.content
document.body.appendChild(content)

  在2.1的例子中我们的Shadow Dom都是在js中拼接的,下面我们将定义的Shadow Dom定义在template中,将其从js中分离出来:

<template id="button-template">
    <style>
      .custom-button{
        border: none;
        cursor: pointer;
        background: hsla(130, 70%, 50%, 1);
        color: #ffffff;
        line-height: 30px;
        border-radius: 5px;
      }
    </style>
    <button class="custom-button"></button>
  </template>
class CustomButton extends HTMLElement{
  constructor() {
    super()
    const shadow = this.attachShadow({mode: 'open'})
    const template = document.querySelector('#button-template')
    const text = this.getAttribute('text')
    const append = template.content.cloneNode(true)
    append.children[1].innerHTML = text
    shadow.appendChild(append)
  }
}

customElements.define('custom-button', CustomButton)

3.2 <slot>

  插槽,作用于vue中的slot相同,可在Web Components中插入DOM结构,下面使用slot修改一下上面的按钮(能被插入到槽中的元素视为 Slotable; 称已经插入到槽中的元素为slotted):

<custom-button3 class="xxx">
	<a slot="my-content" >slot插槽</a>
</custom-button3>
class CustomButton3 extends HTMLElement{
  constructor() {
    super()
    const template = document.querySelector('#button-template').content
    this.attachShadow({mode: 'open'}).appendChild(template.cloneNode(true))
  }
}
customElements.define('custom-button3', CustomButton3)

  关于Slotable的两个属性:

// 获取slot的名字
document.querySelector('custom-button3 a').slot
// 获取Slotable
document.querySelector('custom-button3 a').assignedSlot
// 获取slotted节点
document.querySelector('custom-button3 a').assignedSlot.assignedElements()

示例:https://github.com/MAXLZ1/web-component/tree/master

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MAXLZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值