Javascript组件Web Components自定义html元素
有过.NET Framework开发经验的朋友应该知道,ASP.NET的WebPage有自定义组件开发,以及Winform或WPF都是可开发自定义组件的,在.NET平台上提供了自定义接口,那么在JavaScript同样可以实现自定义元素标签开发,Javascript提供了“HTMLElement”接口提供给开发者不同复合元素的开发,本文讲述JavaScript的自定义元素开发,本文适合有JavaScript面向对象基础入门的读者,否则不适合直阅读本文的示例。
正式进入示例之前,先了解下Web Components概括,下面是来自:“https://developer.mozilla.org/zh-CN/docs/Web/Web_Components ”原文介绍如下:
Web Components 旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。
Custom elements(自定义元素):一组 JavaScript API,允许您定义 custom elements 及其行为,然后可以在您的用户界面中按照需要使用它们。
Shadow DOM(影子 DOM):一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
HTML templates(HTML 模板): <template> 和 <slot> 元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
另外constructor()和super()两个关键字说明:constructor()是一种用于创建和初始化class创建的对象的特殊方法(参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/constructor),super() 关键字用于访问对象字面量或类的原型([[Prototype]])上的属性,或调用父类的构造函数(参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/super)。
示例一
JavaScript声明变量“template”,创建document.createElement('template'),通过“template”预先定义“p”标签样式,在“template”的innerHTML中放入style样式和一个空p标签。接着通过创建一个“ HelloText”类,通过“extends”继承“HTMLElement”, 把“template”内容通过appendChild加载到shadowRoot中“this.shadowRoot.appendChild(template.content.cloneNode(true))”。在 “connectedCallback() ”通过getAttribute("name")加入name属性,最后通过 “customElements”自定义“hello-txt” 元素,window.customElements.define('hello-txt', HelloText),在body中加入“hello-txt”元素标签“<hello-txt name="Hello Everybody!"></hello-txt>”。详细代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Javascript组件Web Components自定义html元素</title>
</head>
<body><script type="text/javascript">
<hello-txt name="Hello Everybody!"></hello-txt>
/*Copyright(C) 遺昕传媒|Weisim3.com 12.19.2022*/
/*Javascript组件Web Components自定义html元素*/
/*示例一*/
const template = document.createElement('template');
template.innerHTML = `
<style>
p {
color:red;
font-weight:bold
}
</style>
<p></p>`;
class HelloText extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.shadowRoot.querySelector('p').innerText = this.getAttribute('name');
}
connectedCallback() {
this.p = this.getAttribute("name")
}
}
window.customElements.define('hello-txt', HelloText);
</script>
</body>
</html>
在浏览器中预览,可以看到“hello-txt”标签有name属性,name值“Hello Everybody!”也就是p标签的文字innerText显示内容,#shadow-root(open)有template中的<style>内容,shadow-root所对应的”attachShadow({ mode: 'open' })“,两个模式”open“和”closed“,closed模式最终在浏览器中无法显示自定义元素内容,效果如下:
示例二:自定复合元素
在示例二中,进一步复杂,通过文本框输入图片地址,返回给img标签显示,并且返回图片的宽高尺寸,而在img显示的缩略图,等比例缩小显示,在触发事件上,先触发按钮onclick将input text 文本框输入的图片地址赋值给img.src,然后需要等比例缩小图片显示处理,这时需要触发img的onload事件,在按钮中触发shadowRoot中的按钮内向父级获取使用“this.parentElement”,而在img的onload中获取则是向父级的父级获取使用“this.parentElement.parentElement”,其它图片缩略显示尺寸思路采用“Html图片列表上下左右居中(ul)”,下面是详细代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<meta-txt name="">
<script type="text/javascript">
/*Copyright(C) 遺昕传媒|Weisim3.com 12.21.2022*/
/*Javascript组件Web Components自定义html元素*/
/*示例二 复合元素*/
const template = document.createElement('template');
template.innerHTML = `
<style>
p {
height:300px;
width:300px;
background-color:antiquewhite;
border:1px dotted red;
}
</style>
<div>
<input id="Text1" type="text" />
<input id="Button1" type="button" value="图片获取" />
<p>
<meta alt=""/>
</p>
<span></span>
</div>`;
class ImgText extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
this.shadowRoot.querySelectorAll("input[type=button]")[0].onclick = function () {
//这里需要指向父级parentElement,此处是在button上出发onclick
this.parentElement.querySelector('img').src = this.parentElement.querySelector('input[type=text]').value;
this.parentElement.querySelector('img').onload = function () {
var widthImg = 300;
var imgLink = this.parentElement.querySelector('img');
//这里需要指向父级的父级this.parentElement.parentElement,此处是在button上出发onclick,然后又触发img的onlaod
this.parentElement.parentElement.querySelector('span').innerHTML = "宽:" + imgLink.width + "px × " + "高:" + imgLink.height+"px";
//alert(imgLink.width + ":" + imgLink.height);
if (imgLink.width > imgLink.height) {
imgLink.width = widthImg;
//imgLink.height
imgLink.style.marginTop = (widthImg - imgLink.height) / 2 + "px";
//alert(imgLink.style.marginTop);
imgLink.style.marginBottom = (widthImg - imgLink.height) / 2 + "px";
//alert(imgLink.height);
}
if (imgLink.width < imgLink.height) {
imgLink.height = widthImg;
//alert(imgLink.height);
}
}
}
}
connectedCallback() {
this.p = this.getAttribute("name")
}
}
window.customElements.define('img-txt', ImgText);
</script>
</body>
</html>
效果如下:
总结:自定义元素封装成模块使用,可以重复多次调用模块,这就是Web Components使用的优势,开发者可以根据不同需要,开发复合组件元素,目前已经支持所有主流浏览器。
window.customElements.define('自定义标签名', 预先定义的类名):固定格式,在自定义内容定义设置好后,创建自定义标签名称,就需要以这句收尾。没有这句就没有实例化,在body中也就无法完成自定义标签使用。
Shadow DOM:可以理解成容器,通过Shadow来访问里面内容,它是不会实际显示在页面中。
HTML templates(HTML 模板): <template> 和 <slot>通过者两个可以设置自定义元素的样式和显示文字内容的排序格式。可以和外部的css样式隔离不受干扰。<template> 和 <slot>在以后的示例文章进一步演示,本文不详细说明。