介绍
您是否曾经想过这些组件是什么以及制作了这样的组件?
这些称为时间轴组件。在这篇博客文章中,我们将探讨这些组件究竟是什么以及如何构建。我们将不再使用通常的React或其他著名框架,而是要稍有扭曲!
构建该组件!我们将使用Web组件技术进行构建。是的,您听到了我正确的网络组件!!。但是,不用担心,我们会简要介绍什么是网络组件,然后直接进入构建时间表组件。
所以,没有更多的延迟,让我们吧!
什么是时间轴组件?
时间轴组件是一个组件,可以帮助我们在特定时间间隔中指示事件的摘要:
时间轴组件的功能
让我们首先建立时间表组件的基本特征:
- 我们的组件将能够将整个时间表与左或右对齐。
- 时间轴中的每个点可以从大小和颜色的角度更改
- 帮助组件消费者为每个时间轴项目添加任意数量的儿童元素。
什么是Web组件?
现在,我们知道了我们正在构建的东西,让我们了解如何构建它。
Web组件是一个标准,可帮助您构建可重复使用的自定义元素。这些自定义元素具有自己的范围和样式,因此可以封装。这样,它们可以在您的代码库中多次使用。这会导致较少的代码碰撞,并且您的自定义元素代码不受其余代码的影响。
因此,从本质上讲,您将在HTML中做类似下面的事情:
<body>
<mycustomcomponent>Hello DOM</mycustomcomponent>
</body>
Web组件是基于三个主要支柱构建的:
- 自定义元素
- Shadow Dom
- HTML模板
要了解这些概念是什么,我强烈建议你们参考MDN docs,因为这些主题不在此博客文章的范围之内。他们在解释它们方面做得很棒。
拟议的建筑
现在,我们知道什么是Web组件,以及如何使用它们来创建我们自己的自定义元素,因此让我们开始使用组件的体系结构。本节将概述我们要构建的内容。
此组件的整个架构如下(请参阅上图):
- 我们将考虑我们的时间表组件是无序的列表,即
ul
HTML元素。我们扩展了ul
的功能。此HTML元素完全满足我们的目的,即显示列表。我们只是要更改此ul
元素的某些外观以实现我们想要的东西。 - 类似于在其中包含
li
元素的原始无序列表类似,我们将遵循相同的方法。时间轴组件中的每个项目都将在内部使用li
元素。 -
ul
元素将被称为timeline-component
- 它需要以下道具:
-
mode
=left
|right
- 此道具有助于将所有项目与
left
或right.
保持一致 - 这意味着所有点都可以在左侧或右侧出现
- 此道具有助于将所有项目与
-
- 它需要以下道具:
-
li
元素将由组件表示:timeline-item
- 每个时间轴项目都可以在其中占用任意数量的儿童元素。
- 每个项目都将隐含地带有一个充当
dot
的DIV元素。 - 此组件具有以下属性:
-
dotcolor
:可以是任何RGB值,也可以是字符串值。 -
dotsize
:larget
medium
small
-
从本质上讲,我们想做的是以下类似:
<body>
<timeline-component mode="left">
<timeline-item dotcolor="red" dotsize="large">
<span>Event 1</span>
</timeline-item>
<timeline-item dotcolor="red" dotsize="large">
<span>Event 2</span>
</timeline-item>
</body>
现在,我们必须了解整个组件的基本体系结构,让我们开始构建它ð
为项目创建脚手架
开始之前,让我们创建一个简单的项目。该项目将具有以下文件:
-
src
index.js
styles.css
index.html
我们应该在您选择的项目目录中创建上述文件和目录,否则您只需创建Vanilla JavaScript的codesandbox,然后开始hacking
实施详细信息
时间轴组件
首先,让我们定义index.html
文件的结构。将以下内容放在其中:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link type="stylesheet" href="./src/styles.css"></link>
</head>
<body>
<div id="app">
</div>
<script src="src/index.js"></script>
</body>
</html>
对于创建Web组件,我们使用模板元素是一种常见的做法。使用它的优点是我们可以在HTML文件中写入它,但是它不会立即在实际DOM上渲染。我们需要使用JavaScript API定义它。
让我们创建一个模板元素如下:
<template id="timeline-component">
<style>
ul {
list-style: none;
padding-left: 0px;
margin: 0px;
position: relative;
}
:host([mode="left"]) ul {
direction: ltr;
border-left: 2px solid #cacaca;
border-right: 0;
}
:host([mode="right"]) ul {
border-right: 2px solid #cacaca;
border-left: 0;
direction: rtl;
}
</style>
<ul>
<slot></slot>
</ul>
</template>
此模板元素适用于我们的timeline-component
。在提议的体系结构部分中,我们讨论了我们将为此组件使用无序的列表ul
。所以我们将做同样的事情。
我们使用了模板元素内的ul
元素,然后将slot
元素放置在此标签内。老虎机是从light DOM中吸收标记的占位符。您可以从MDN docs中阅读有关插槽的更多信息。
我们在这里使用默认插槽。默认插槽是没有名称的插槽。它从在其他地方没有插入的光DOM获取所有节点。
在此模板元素中,我们还添加了一个样式标签。我们以其样式定义了ul
元素的样式。每当定义timeline-component
时,这种样式都会加载。此处定义的样式将保留在timeline-component
本身的范围内。因此,外面的样式不会影响这些样式。因此,自定义元素可以帮助我们实现范围的样式。
此ul
元素具有以下基础样式:
ul {
list-style: none;
padding-left: 0px;
margin: 0px;
position: relative;
}
ul
元素的样式也基于mode
属性。每当将此属性设置为left
时,我们都希望以下CSS应用于ul
元素:
:host([mode="left"]) ul {
direction: ltr;
border-left: 2px solid #cacaca;
border-right: 0;
}
,当mode
设置为right
时,我们希望它具有以下CSS:
:host([mode="right"]) ul {
border-right: 2px solid #cacaca;
border-left: 0;
direction: rtl;
}
这有助于我们将内容与DOT一起移动到左右。我们在模板组件本身中定义这些样式。我们利用:host CSS selector。此选择器用于选择主机节点,即<timeline-component>
本身。
|
|
---|
现在是时候定义此模板元素了。让我们创建一个名为:index.js
的文件,然后将以下内容放入其中:
class TimelineComponent extends HTMLElement {
constructor() {
super();
const template = document.querySelector("#timeline-component").content;
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(template.cloneNode(true));
}
}
要定义timeline-component
,我们需要在index.js
文件中创建一个类。该课的责任是:
- 在初始化/页面加载期间找到模板元素
- 创建阴影根。
- 将模板节点连接到此阴影根。
让我们首先了解在页面加载或组件初始化期间将要做什么。当我们创建一个名为:TimelineComponent
的新类时,我们使用其构造函数来完成以下操作:
- 我们从
HTMLElement
类扩展了此组件。 - 然后我们利用
super
函数来调用HTMLElement
的所有属性和功能。 -
接下来,我们获取使用ID创建的模板组件的所有内容:
timeline-component
。
const template = document.querySelector("#timeline-component").content;
-
接下来,我们使用以下行创建一个Open
shadowRoot
:
const shadowRoot = this.attachShadow({ mode: "open" });
-
最后,我们将模板元素的所有内容附加到此阴影根:
shadowRoot.appendChild(template.cloneNode(true));
现在我们已经准备好组件,让我们定义此自定义元素。您可以通过调用customElements
API的define
方法来执行此操作。此方法以您或组件的消费者在其HTML内使用的组件的名称,并将其映射到我们创建的上述类中。我们这样做如下:
customElements.define("timeline-component", TimelineComponent);
时间轴项
该组件用于表示我们提出的体系结构中所见的li
元素。该组件将帮助消费者显示任意数量的儿童元素。该元素将由与我们之前看到的dot
相对应的DIV元素组成。该组件具有控制该点元素的行为的能力。
timeline-item
将接受以下道具:
-
dotsize
:large
medium
small
-
dotcolor
:可以是任何RGB值,也可以是字符串值。
我们开始创建和定义此自定义元素之前首先让我们为相同的模板元素创建模板元素。
<template id="timeline-item">
<style>
li {
margin-left: 1rem;
margin-bottom: 2.5rem;
}
.dot {
border: 7px solid #cacaca;
display: inline-block;
border-radius: 50%;
position: absolute;
}
</style>
<li>
<div class="dot"></div>
<slot></slot>
</li>
</template>
此模板元素由我们在提议的体系结构中看到的li
元素组成。该元素具有div
元素,可作为timeline-item
组件的点。我们使用dotsize
和dotcolor
属性更改此点元素的大小和颜色。
我们再次利用样式元素为此元素具有范围的样式。
我们利用默认的插槽机制将timeline-item
组件的所有子元素放在上面定义的插槽中。例如,
<timeline-item dotcolor="blue" dotsize="large">
<h1>23rd Jan 2023</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est
aliquid nesciunt reiciendis esse accusamus laudantium, assumenda
praesentium illo nemo ratione at itaque voluptatum cum. Veniam at
magnam itaque harum expedita.
</p>
</timeline-item>
在这里,h1
和p
元素将成为我们上面定义的插槽的一部分。
现在我们已经为此组件创建了template
元素,让我们从创建和定义此自定义元素开始。
我们创建此组件的途径将与。在同一index.js
文件中,创建一个名为:TimelineItem
的新类,并粘贴其中的以下内容:
const DOT_SIZE = {
small: "7px",
medium: "10px",
large: "12px"
};
const DOT_SIZE_TO_POS_MAP = {
small: "-0.5rem",
medium: "-0.7rem",
large: "-0.8125rem"
};
class TimelineItem extends HTMLElement {
static get observedAttributes() {
return ["dotcolor", "dotsize"];
}
constructor() {
super();
const template = document.querySelector("#timeline-item").content;
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.appendChild(template.cloneNode(true));
}
attributeChangedCallback(e, oldValue, newValue) {
const dotElement = this.shadowRoot.querySelector(".dot");
const modeAttribute = this.parentElement.getAttribute("mode"); // get parent element attribute;
if (e === "dotcolor") {
dotElement.style.borderColor = newValue;
}
if (e === "dotsize") {
if (modeAttribute === "left") {
dotElement.style.borderWidth = DOT_SIZE[newValue];
dotElement.style.left = DOT_SIZE_TO_POS_MAP[newValue];
} else if (modeAttribute === "right") {
dotElement.style.borderWidth = DOT_SIZE[newValue];
dotElement.style.left = "";
dotElement.style.right = DOT_SIZE_TO_POS_MAP[newValue];
}
}
}
}
类似于我们在timeline-component
中所做的事情,我们在TimelineItem
类中进行以下操作:
-
我们利用构造函数进行以下操作:
- 调用
super
方法以初始化其基类的所有元素。 -
获取我们创建的模板元素的所有内容:
const template = document.querySelector("#timeline-item").content;
-
我们创建一个开放的Shadowroot节点,并在此Shadowroot中附加模板节点的所有内容:
const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.appendChild(template.cloneNode(true));
- 调用
-
为了使
timeline-item
观察dotsize
和dotcolor
属性,我们需要利用返回要观察的属性数组的observedAttribute
静态方法。我们必须如下这样做:
static get observedAttributes() { return ["dotcolor", "dotsize"]; }
我们使用这种静态Getter方法,因为当这些属性更改时,我们希望执行某些操作。为了执行这些操作,我们使用
attributeChangedCallback
回调。每当在这种情况下是dotcolor
和dotsize
的观察属性更改时,此回调将被执行。
static get observedAttributes() { return ["dotcolor", "dotsize"]; } attributeChangedCallback(e, oldValue, newValue) { const dotElement = this.shadowRoot.querySelector(".dot"); const modeAttribute = this.parentElement.getAttribute("mode"); // get parent element attribute; if (e === "dotcolor") { dotElement.style.borderColor = newValue; } if (e === "dotsize") { if (modeAttribute === "left") { dotElement.style.borderWidth = DOT_SIZE[newValue]; dotElement.style.left = DOT_SIZE_TO_POS_MAP[newValue]; } else if (modeAttribute === "right") { dotElement.style.borderWidth = DOT_SIZE[newValue]; dotElement.style.left = ""; dotElement.style.right = DOT_SIZE_TO_POS_MAP[newValue]; } } }
- 这里要注意的一件事是,我们访问父元素的
mode
属性以样式的组件。在这里,父组件将是timeline-component
- 每当
dotcolor
更改时,我们将点元素的边框颜色设置为属性值。 -
每当
dotsize
更改时,我们都会进行以下更改:- 如果父元素的
mode
属性是left
,那么我们设置了点元素宽度及其左CSS属性。 - 我们利用
DOT_SIZE
和DOT_SIZE_TO_POS_MAP
地图来管理元素的边界偏转和位置。 -
这些地图具有以下默认值:
const DOT_SIZE = { small: "7px", medium: "10px", large: "12px" }; const DOT_SIZE_TO_POS_MAP = { small: "-0.5rem", medium: "-0.7rem", large: "-0.8125rem" };
- 如果父元素的
- 这里要注意的一件事是,我们访问父元素的
-
最后,我们利用
customElements
API的define
方法来定义以下元素:
customElements.define("timeline-item", TimelineItem);
用法
现在我们已经实施了这些组件,现在是时候使用这些组件了。将以下代码放在index.html
文件中:
<timeline-component mode="left">
<timeline-item dotcolor="blue" dotsize="large">
<h1>23rd Jan 2023</h1>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est
aliquid nesciunt reiciendis esse accusamus laudantium, assumenda
praesentium illo nemo ratione at itaque voluptatum cum. Veniam at
magnam itaque harum expedita.
</p>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est
aliquid nesciunt reiciendis esse accusamus laudantium, assumenda
praesentium illo nemo ratione at itaque voluptatum cum. Veniam at
magnam itaque harum expedita.
</p>
</timeline-item>
<timeline-item dotcolor="orange" dotsize="medium">
<h2>Event 2</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Est
aliquid nesciunt reiciendis esse accusamus laudantium, assumenda
praesentium illo nemo ratione at itaque voluptatum cum. Veniam at
magnam itaque harum expedita.
</p>
</timeline-item>
</timeline-component>
您可以在此博客中找到有关此codesandbox的所有代码和工作示例。
概括
因此,通过这种方式,您可以通过利用已经存在的ul
HTML元素来构建自己的时间表组件。我们还触及了Web组件技术的一些基础知识:自定义元素,插槽和模板。但是我强烈建议你们浏览这些概念的MDN文档。
未来的工作和进一步阅读:
- 您可以考虑使用以下功能扩展此组件:
- 创建一个接受SVG图标的自定义点元素,类似于Mui所做的here。
- 对齐中心的时间轴组件。
- 相反的内容功能。
谢谢您的阅读!