Web Components 封装一个table组件

Web Components

这个table组件是我很久以前就想搞的了 一直拖到现在 今天终于出来了 原因也是最近才找到方法 说白了就是菜吧 最近接触了一个东西 叫 web component

Web Components

Web Components 是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。

后面的就不说了 你们可以看看文档 这时候说回来 一直迷恋于封装组件的我第一眼看到就觉得有搞头 然后开始研究它 当然现在的我还是一直在爬坑 下面的组件封装过程也只是我的个人理解 整个组件并不完善 只是某个阶段

table组件 模仿element table组件模式 先看看html 部分

table.html
sql 复制代码
<hd-table class="hd-table">
	<hd-table-column type="index" label="日期" width="180"></hd-table-column>
	<hd-table-column prop="date" label="日期" width="180"></hd-table-column>
	<hd-table-column prop="name" label="姓名" width="180"></hd-table-column>
	<hd-table-column prop="address" label="地址" width="180"></hd-table-column>
</hd-table>
bash 复制代码
    <script type="text/javascript" src="./table.js"></script>
    <script type="text/javascript">
    	let data = [{
        date: '2016-05-03',
        name: '王小虎1',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-02',
        name: '王小虎2',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-04',
        name: '王小虎3',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-01',
        name: '王小虎4',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-08',
        name: '王小虎5',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-06',
        name: '王小虎6',
        address: '上海市普陀区金沙江路 1518 弄'
      }, {
        date: '2016-05-07',
        name: '王小虎7',
        address: '上海市普陀区金沙江路 1518 弄'
      }]
      let hdTable = document.getElementsByTagName('hd-table')[0]
      // console.log(hdTable)
      hdTable.setAttribute('data', JSON.stringify(data))
    </script>
table.css
css 复制代码
* {
	margin: 0;
	padding: 0;
}
.hd__table {
	width: 100%;
	position: relative;
	box-sizing: border-box;
	max-width: 100%;
	font-size: 14px;
	color: #606266;
	/*flex: 1;*/
}
.hd__table:before{
	content: "";
  position: absolute;
  background-color: #ebeef5;
  z-index: 1;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
}
.hd__table-header-wrapper {
	width: 100%;
	overflow: hidden;
}
.hd__table-header {
	width: 100%;
	border-collapse: separate;
	table-layout:fixed;
}
.hd__table-header tr {
	background-color: #fff;
	color: #909399;
  font-weight: 500;
  box-sizing: border-box;
}
.hd__table-header tr > th {
	box-sizing: border-box;
	padding: 12px 0;
	text-align: left;
	text-overflow: ellipsis;
  vertical-align: middle;
	/*text-align: center;*/
	border-bottom: 1px solid #ebeef5;
	/*border-right: 1px solid #ebeef5;*/
}
.hd__table-header tr > th > .cell {
	padding: 0 10px;
	width: 100%;
	box-sizing: border-box;
	overflow: hidden;
    text-overflow: ellipsis;
    white-space: normal;
    word-break: break-all;
    line-height: 23px;
}
.hd__table-body-wrapper {
	overflow: hidden;
  position: relative;
  width: 100%;
}
.hd__table-body {
	width: 100%;
	border-collapse: separate;
	table-layout:fixed;
	/*display: none;*/
}
.hd__table-row {
	background-color: #fff;
}
.hd__table-row td {
	box-sizing: border-box;
	padding: 12px 0;
	text-align: left;
	text-overflow: ellipsis;
    vertical-align: middle;
	/*text-align: center;*/
	border-bottom: 1px solid #ebeef5;
	/*border-right: 1px solid #ebeef5;*/
}
.hd__table-row > td > .cell {
	padding: 0 10px;
	width: 100%;
	box-sizing: border-box;
	overflow: hidden;
    text-overflow: ellipsis;
    white-space: normal;
    word-break: break-all;
    line-height: 23px;
}
.hd__table-empty {
	width: 100%;
	min-height: 60px;
	height: 100%;
	text-align: center;
	display: flex;
	justify-content: center;
	align-items: center;
}
.hd__table-empty-none {
	display: none;
}

开始进入正题

table.js
ini 复制代码
class Component extends HTMLElement {
	static get observedAttributes() {return ['data'] }
	constructor() {
	  super();
	  this.init();
  }
  init() {
  	// 顶层影子DOM
    let shadow = this.attachShadow({
      mode: 'open'
    });
    this.state = {};
  }
}

// <hd-table></hd-table> 
class HdTable extends Component {
	constructor() {
    super();
    this._style()
    this._html()
    this.data()
    this.render()
  }
  // 初始化数据
  data() {
  	this.state = {
  		data: []
  	}
  	try {
  		if (this.getAttribute('data')) {
        this.state.data = JSON.parse(this.getAttribute('data'));
      }
  	} catch(e) {
  		this.state.data = [];
  	}
  }
  // 样式引入
	_style() {
		// 在shadowRoot创建css 
		let tableCss = document.createElement('link')
	  tableCss.rel = 'stylesheet';
	  tableCss.type = 'text/css';
	  tableCss.href = this._link() + 'table.css';
	  this.shadowRoot.appendChild(tableCss)
	}
  // 获取路径
	_link() {
		let b, a, f = window['document']['getElementsByTagName']("script");
	  for (let c = 0; c < f.length; c++) {
      b = f[c].getAttribute("src") || "";
      b = b.substr(0, b.toLowerCase().indexOf("table.js"));
      a = b.lastIndexOf("/");
      if (a > 0) {
          b = b.substring(0, a + 1)
      }
      if (b) {
          break;
      }
	  }
	  return b 
	}
	// html
	_html() {
		let container = document.createElement('div')
  	container.classList.add('hd__table')
  	container.innerHTML = `
  		<div class="hd__table-header-wrapper"></div>
  		<div class="hd__table-body-wrapper">
  			<table class="hd__table-body" border="0" cellspacing="0" cellpadding="0">
  				<tr class="hd__table-row">
  					<slot></slot>
  				</tr>
  			</table>
 			<div class="hd__table-empty hd__table-empty-none">
 				<span>暂无数据</span>
 			</div>
  		</div>
  	`
  	this.shadowRoot.appendChild(container);
	}
	render() {
		let header = this.shadowRoot.querySelector('.hd__table-header-wrapper');
		header.innerHTML = this._htmlHeader()
		let tableBody = this.shadowRoot.querySelector('.hd__table-body-wrapper');
		// tableBody.innerHTML = this._htmlColumn()
		let column = this._htmlColumn()
		if(column) {
			tableBody.innerHTML = this._htmlColumn()
		}
	}
	// 获取有几个hd-table-column标签
	getTableColumnList() {
		let childrenList = this.children
		let tableColumnList = []
		Object.keys(childrenList).forEach(function(key){
    	if(childrenList[key].nodeName == 'HD-TABLE-COLUMN') {
    		// childrenList[key]
    		let prop = childrenList[key].getAttribute('prop')
    		let label = childrenList[key].getAttribute('label')
    		let width = childrenList[key].getAttribute('width')
    		let type = childrenList[key].getAttribute('type')
     		tableColumnList.push({ prop, label, width, type })
     	}
		})
		this.state.tableColumnList = tableColumnList
	}

	_htmlHeader() {
		this.getTableColumnList()
		let str = this.state.tableColumnList.map((item, i) => {
			if(item.type == 'index') {
				return `<th><div class="cell"></div></th>`
			} else if(item.type == "selection") {
				return `<th><div class="cell"></div></th>`
			} else {
				return `<th><div class="cell">${item.label}</div></th>`
			}
		}).join('');
		return `<table class="hd__table-header" border="0" cellspacing="0" cellpadding="0"><tr>${str}</tr></table>`
	}
	_htmlColumn() {
		let empty = this.shadowRoot.querySelector('.hd__table-empty')
		if(this.state.data.length == 0) {
			empty.classList.remove('hd__table-empty-none')
			return
		} else {
			empty.classList.add('hd__table-empty-none')
			let row = this.shadowRoot.querySelector('.hd__table-row')
			row.innerHTML = this._htmlRow()
			let rows = this.state.data.map((item,index)=>{
				let rowsChild = row.querySelectorAll('td .cell')
				rowsChild.forEach(items => {
					let type = items.getAttribute('type')
					let prop = items.getAttribute('prop')
					if(type == 'index') {
						items.innerHTML = index
					} else if(type == 'selection') {
						items.innerHTML = ''
					} else {
						items.innerHTML = item[prop]
					}
				})
				return `<tr class="hd__table-row">${row.innerHTML}</tr>`
			}).join('')
			return `<table class="hd__table-body" border="0" cellspacing="0" cellpadding="0">${rows}</table>`
		}
	}
	_htmlRow() {
		this.getTableColumnList()
		let str = this.state.tableColumnList.map(item =>{
			if(item.type == 'index' || item.type == 'selection') {
				return `
	  	 		<td rowspan="1" colspan="1" type="${item.type} prop="${item.prop} width="${item.width}">
			  		<div class="cell" type="${item.type}"></div>
			  	</td>`
			} else {
				return `
	  	 		<td rowspan="1" colspan="1" type="${item.type} prop="${item.prop} width="${item.width}">
			  		<div class="cell" prop="${item.prop}"></div>
			  	</td>`
			}
		}).join('');
		return str
    }
    // 当监听的值发送变化时
	attributeChangedCallback(attrName, oldVal, newVal) {
		if(attrName == 'data') {
			this.state.data = JSON.parse(newVal)
			let tableBody = this.shadowRoot.querySelector('.hd__table-body-wrapper');
			tableBody.innerHTML = this._htmlColumn()
		}
	}
}
customElements.define('hd-table',HdTable);

这么看下来 就有点像react 不过这个是原生哦 很多东西我没办法写一个很好的注释去解释 因为我的基础很差 写了也只能说是我自己的理解 容易误导人 索性也就不写了 还是先来解释解释Web Components 还是再起一篇文章解释吧

最后最后给自己来句鸡汤吧 毕竟搞出来也花了不少的功夫 爬了不少的坑

别害怕,委屈只是暂时的,路会越走越亮;你年纪轻轻,心地善良,你怕什么。总有一天,你会站在最亮的地方,活成自己曾经最渴望的样子。

相关推荐
小马哥编程1 小时前
Function.prototype和Object.prototype 的区别
javascript
王小王和他的小伙伴1 小时前
解决 vue3 中 echarts图表在el-dialog中显示问题
javascript·vue.js·echarts
学前端的小朱1 小时前
处理字体图标、js、html及其他资源
开发语言·javascript·webpack·html·打包工具
outstanding木槿1 小时前
react+antd的Table组件编辑单元格
前端·javascript·react.js·前端框架
好名字08212 小时前
前端取Content-Disposition中的filename字段与解码(vue)
前端·javascript·vue.js·前端框架
摇光932 小时前
js高阶-async与事件循环
开发语言·javascript·事件循环·宏任务·微任务
胡西风_foxww3 小时前
【ES6复习笔记】Class类(15)
javascript·笔记·es6·继承··class·静态成员
布兰妮甜3 小时前
使用 WebRTC 进行实时通信
javascript·webrtc·实时通信
艾斯特_3 小时前
JavaScript甘特图 dhtmlx-gantt
前端·javascript·甘特图
飞翔的渴望3 小时前
react18与react17有哪些区别
前端·javascript·react.js