首先可以点击右边的连接来体验一下最终的效果:Excel表格
这里我罗列出我主要实现的五大功能:
- 行列以及cell的hover和click效果
- 增删行列
- resize功能
- 鼠标选择效果
- 支持编辑
下面我给出一些主要功能以及棘手问题的解决方案:
1.表格的设计:
采取table布局天生与Excel相匹配。
2.外观样式的设计:
用定位技术来确定两个中心div(这两div是父子关系),一个div固定大小只改变座标,而另一个div两者都改变。并给其增加渐变的动画效果来提高切换状态时流畅度。
3.制作行列以及cell的click事件时如何找到想要的位置:
给一行的每一个cell绑定不同的额外属性,例如data-index=“A”,目的就是为了建立cell与header的直接联系。(默认的表格只有逻辑上的关系,却没有实质上的dom关系)
4.实现resize:
给每一个需要支持resize行为的元素绑定一个绝对定位的div,然后让此div绑定鼠标的down、move、up事件,拖动此div使得其平行移动的距离添加其对应的父元素即可。(注意由于我这里的table默认的固定大小,所以也需要给table增减长度)
5.增删行列:
绑定行列右键的点击事件进行增删事件操作,增加就是插入元素,删除就是移除元素。由于table没有dom上的直接关系所以在增加行时为了保证表结构的完整只能手动append元素。(注意:由于如果文本内容是div的父元素会导致在进行一次增删操作后,div被吃掉的现象,所以采取用span包裹文本使得其与div是兄弟关系)
6.实现选择效果:
这里就需要绑定每个cell的鼠标down、move、up事件。
首先我们假定操作的方向分为上下左右,(为什么是上下左右而不是左上左下右上右下呢,因为以左上为例就可以分解为先左后上或者先上后左),然后找到矩形的两个端点cell并开始绘制矩形区域。这里我把所有的绘制矩形的情况都综合成一个方法—两点定位法。
所谓两点定位法简单来说就是任何一个矩形都能由其对角线的两个端点cell来决定此矩形的方位和大小,更神奇的是不管矩形怎么边,总有一个cell就是另外一个div所在的位置。
7.支持编辑:
给每一个cell绑定一个双击事件,每个事件里绝对定位一个input输入框并使得其呈现在最上方,在完成输入后及时移除掉此input输入框。
8.css层叠样式:
为了尽可能的减少重复代码以及js与css的耦合,可以将一些固定的样式或者固定改变的样式定位为一种或多种css选择器甚至直接使用css样式重叠的功能来实现效果。
以上我只给出了一些主要难点的解决方案,而更多细节问题我就在此就不多解释,应该都能通过代码理解。这个小项目我也是花了近两周时间查阅无数次资料并多次请教大佬才得以完成,所以如果有代码看不懂的地方可以直接在评论区留言,或者有更妙的想法也欢迎评论区留言。
话不多说,直接上代码。(这次的代码量会比之前要多很多)
html代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Excel</title>
<link rel="stylesheet" href="excel.css">
</head>
<body>
<div class="blank">
<a href="javascript:void(0)" class="add">Add</a>
<a href="javascript:void(0)" class="remove">Remove</a>
</div>
<table class="table">
<tr class="head">
<th class="both">/</th>
<th class="header"><span>A</span><div class="resizeE"></div>
</th>
<th class="header"><span>B</span><div class="resizeE"></div>
</th>
<th class="header"><span>C</span><div class="resizeE"></div>
</th>
<th class="header"><span>D</span><div class="resizeE"></div>
</th>
<th class="header"><span>E</span><div class="resizeE"></div>
</th>
<th class="header"><span>F</span><div class="resizeE"></div>
</th>
<th class="header"><span>G</span><div class="resizeE"></div>
</th>
<th class="header"><span>H</span><div class="resizeE"></div>
</th>
<th class="header"><span>I</span><div class="resizeE"></div>
</th>
<th class="header"><span>J</span><div class="resizeE"></div>
</th>
<th class="header"><span>K</span><div class="resizeE"></div>
</th>
<th class="header"><span>L</span><div class="resizeE"></div>
</th>
<th class="header"><span>M</span><div class="resizeE"></div>
</th>
<th class="header"><span>N</span><div class="resizeE"></div>
</th>
<th class="header"><span>O</span><div class="resizeE"></div>
</th>
<th class="header"><span>P</span><div class="resizeE"></div>
</th>
<th class="header"><span>Q</span><div class="resizeE"></div>
</th>
<th class="header"><span>R</span><div class="resizeE"></div>
</th>
<th class="header"><span>S</span><div class="resizeE"></div>
</th>
<th class="header"><span>T</span><div class="resizeE"></div>
</th>
</tr>
<tr>
<td class="rowHeader"><span>1</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
<tr>
<td class="rowHeader"><span>2</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
<tr>
<td class="rowHeader"><span>3</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
<tr>
<td class="rowHeader"><span>4</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
<tr>
<td class="rowHeader"><span>5</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
<tr>
<td class="rowHeader"><span>6</span><div class="resizeS"></div>
</td>
<td class="cell" data-index="A"></td>
<td class="cell" data-index="B"></td>
<td class="cell" data-index="C"></td>
<td class="cell" data-index="D"></td>
<td class="cell" data-index="E"></td>
<td class="cell" data-index="F"></td>
<td class="cell" data-index="G"></td>
<td class="cell" data-index="H"></td>
<td class="cell" data-index="I"></td>
<td class="cell" data-index="J"></td>
<td class="cell" data-index="K"></td>
<td class="cell" data-index="L"></td>
<td class="cell" data-index="M"></td>
<td class="cell" data-index="N"></td>
<td class="cell" data-index="O"></td>
<td class="cell" data-index="P"></td>
<td class="cell" data-index="Q"></td>
<td class="cell" data-index="R"></td>
<td class="cell" data-index="S"></td>
<td class="cell" data-index="T"></td>
</tr>
</table>
<script src="excel.js"></script>
</body>
</html>
css代码:
*{
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
}
.blank{
width: 100%;
height: 180px;
background-color: rgb(27, 189, 94);
}
a{
position: absolute;
visibility: hidden;
padding: 5px;
width: 100px;
font-family: Arial;
color: white;
border-radius: 10px;
text-align: center;
text-decoration: none;
}
.add{
top:80px;
left:700px;
background-color: rgb(57, 190, 243);
}
.remove{
top:120px;
left:700px;
background-color: #f14343;
}
.add:hover{
background: rgb(8, 168, 231);
}
.remove:hover{
background-color: #c90707;
}
.table{
margin: 0;
cursor: cell;
position: relative;
border-collapse:collapse;
}
.header {
position: relative;
min-width: 64px;
line-height: 25px;
min-height: 25px;
max-height: 25px;
text-align: center;
font-size: 10px;
border: 1px solid rgb(194, 194, 194);
background-color: rgb(226, 225, 225);
}
.resizeE {
position: absolute;
cursor: e-resize;
width: 16px;
height: 100%;
right: -8px;
top:0;
z-index: 10;
}
.resizeS{
position: absolute;
cursor: s-resize;
height: 10px;
width: 100%;
bottom: -5px;
left:0;
z-index: 10;
}
.header:hover{
background-color: rgb(137, 202, 124) !important;
cursor: s-resize !important;
}
.both{
min-width: 36px;
min-height: 26px;
max-width: 36px;
max-height: 26px;
line-height: 26px;
text-align: center;
font-size: 14px;
background-color: rgb(226, 225, 225);
}
.cell{
min-width: 64px;
min-height: 25px;
line-height: 25px;
position: relative;
text-align: left;
border: 1px solid rgb(194, 194, 194);
overflow: visible;
}
.rowHeader {
position: relative;
min-width: 36px;
max-width: 36px;
min-height: 25px;
line-height: 25px;
text-align: center;
font-size: 14px;
border: 1px solid rgb(194, 194, 194);
background-color: rgb(226, 225, 225);
}
.rowHeader:hover{
background-color: rgb(137, 202, 124) !important;
cursor: w-resize;
}
.selected {
background-color: rgb(185, 185, 185);
}
.header.selected {
border-bottom-color: rgb(21, 104, 10);
}
.rowHeader.selected {
border-right-color: rgb(21, 104, 10);
}
.active {
background-color: rgb(162, 224, 150);
}
js代码:
var downScreenX = 0;
var downScreenY = 0;
var oldMoveScreenX = 0;
var oldMoveScreenY = 0;
var newMoveScreenX = 0;
var newMoveScreenY = 0;
var startResizeLeft = 0;
var startCellFlag = false;
var startResizeEsFlag = false;
var startResizeSsFlag = false;
var headerChange = null;
var rowHeaderChange = null;
var EDivHeader = null;
var SDivHeader = null;
var head = document.getElementsByClassName('head')[0];
var tableX = head.clientWidth;
var length = head.cells.length;
var table = document.getElementsByClassName('table')[0];
var cells = document.getElementsByClassName('cell');
var headers = document.getElementsByClassName('header');
var rowHeaders = document.getElementsByClassName("rowHeader");
var both = document.getElementsByClassName("both")[0];//table最左上角的那个元素
var addButton = document.getElementsByClassName('add')[0];
var removeButton = document.getElementsByClassName('remove')[0];
var resizeEs = document.getElementsByClassName('resizeE');
var resizeSs = document.getElementsByClassName('resizeS');
//大框框
var divElement = document.createElement('div');
//小框框
var fixDivElement = document.createElement('div');
//保存大框框的两个对角cell
var divStatus = [];
divStatus[0] = cells[0];
divStatus[1] = cells[0];
appendDiv();
appendFixDiv();
for (var i = 0; i < resizeEs.length; i++) {
resizeEs[i].addEventListener('mousedown', resizeEsDownHandler, false);
}
for (var i = 0; i < resizeSs.length; i++) {
resizeSs[i].addEventListener('mousedown', resizeSsDownHandler, false);
}
for (var i = 0; i < cells.length; i++) {
cells[i].addEventListener('click', cellHandler, false);
cells[i].addEventListener('dblclick', dblCellHandler, false);
cells[i].addEventListener('mousedown', cellDownHandler, false);
cells[i].addEventListener('mouseup', cellUpHandler, false);
cells[i].addEventListener('mousemove', cellMoveHandler, false);
}
for (var i = 0; i < headers.length; i++) {
headers[i].addEventListener('click', headerHandler, false);
headers[i].addEventListener('contextmenu', headerMenuHandler, false);
headers[i].addEventListener('mouseup', headerUpHandler, false);
}
headers[0].classList.add('selected');
for (var i = 0; i < rowHeaders.length; i++) {
rowHeaders[i].addEventListener('click', rowHeaderHandler, false);
rowHeaders[i].addEventListener('contextmenu', rowHeaderMenuHandler, false);
rowHeaders[i].addEventListener('mouseup', rowHeaderUpHandler, false);
}
rowHeaders[0].classList.add('selected');
both.addEventListener('click', bothHandler, false);
function appendDiv() {
divElement.style.position = 'fixed';
divElement.style.transitionProperty = 'all';
divElement.style.transitionDuration = '0.2s';
divElement.style.pointerEvents = 'none';
divElement.style.width = cells[0].getClientRects()[0].width + 'px';
divElement.style.height = cells[0].getClientRects()[0].height + 'px';
divElement.style.border = '1.5px solid rgb(21, 104, 10)';
divElement.style.top = cells[0].getBoundingClientRect().top + 'px';
divElement.style.left = cells[0].getBoundingClientRect().left + 'px';
divElement.style.backgroundColor = 'rgba(83, 83, 83, 0.3)';
table.appendChild(divElement);
}
function appendFixDiv() {
fixDivElement.style.position = 'fixed';
fixDivElement.style.transitionProperty = 'all';
fixDivElement.style.transitionDuration = '0.2s';
fixDivElement.style.pointerEvents = 'none';
fixDivElement.style.width = cells[0].getClientRects()[0].width - 4 + 'px';
fixDivElement.style.height = cells[0].getClientRects()[0].height - 4 + 'px';
fixDivElement.style.top = cells[0].getBoundingClientRect().top + 1.5 + 'px';
fixDivElement.style.left = cells[0].getBoundingClientRect().left + 1.5 + 'px';
fixDivElement.style.border = 'none';
fixDivElement.style.backgroundColor = 'white';
fixDivElement.style.zIndex = '2';
divElement.appendChild(fixDivElement);
}
//全篇最神奇的函数 绘制大框框
function changeDiv() {
divElement.style.height = Math.max(divStatus[1].getBoundingClientRect().bottom - divStatus[0].getBoundingClientRect().top, divStatus[0].getBoundingClientRect().bottom - divStatus[1].getBoundingClientRect().top) + 'px';
divElement.style.width = Math.max(divStatus[1].getBoundingClientRect().right - divStatus[0].getBoundingClientRect().left, divStatus[0].getBoundingClientRect().right - divStatus[1].getBoundingClientRect().left) + 'px';
divElement.style.top = Math.min(divStatus[0].getBoundingClientRect().top, divStatus[1].getBoundingClientRect().top) + 'px';
divElement.style.left = Math.min(divStatus[0].getBoundingClientRect().left, divStatus[1].getBoundingClientRect().left) + 'px';
changeFixDiv();
fixDivText(divStatus[0]);
}
//绘制小框框
function changeFixDiv() {
fixDivElement.style.width = divStatus[0].getBoundingClientRect().width - 4 + 'px';
fixDivElement.style.height = divStatus[0].getBoundingClientRect().height - 4 + 'px';
fixDivElement.style.top = divStatus[0].getBoundingClientRect().top + 1.5 + 'px';
fixDivElement.style.left = divStatus[0].getBoundingClientRect().left + 1.5 + 'px';
}
function resizeEsDownHandler(e) {
hidden();
document.addEventListener('mousemove', resizeEsMoveHandler, false);
document.addEventListener('mouseup', resizeEsUpHandler, false);
var rect = e.srcElement.getBoundingClientRect();
EDivHeader = e.srcElement.parentElement;
startResizeLeft = rect.left;
startResizeEsFlag = true;
downScreenX = e.screenX;
divElement.style.visibility = 'hidden';
fixDivElement.style.visibility = 'hidden';
}
function resizeSsDownHandler(e) {
hidden();
document.addEventListener('mousemove', resizeSsMoveHandler, false);
document.addEventListener('mouseup', resizeSsUpHandler, false);
var rect = e.srcElement.getBoundingClientRect();
SDivHeader = e.srcElement.parentElement.parentElement;
startResizeTop = rect.top;
startResizeSsFlag = true;
downScreenY = e.screenY;
divElement.style.visibility = 'hidden';
fixDivElement.style.visibility = 'hidden';
}
function resizeEsUpHandler(e) {
changeDiv();
divElement.style.visibility = 'visible';
fixDivElement.style.visibility = 'visible';
if (startCellFlag) {
startCellFlag = false;
}
if (startResizeEsFlag) {
startResizeEsFlag = false;
downScreenX = 0;
oldMoveScreenX = 0;
newMoveScreenX = 0;
EDivHeader = null;
document.removeEventListener('mousemove', resizeEsMoveHandler, false);
document.removeEventListener('mouseup', resizeEsUpHandler, false);
}
}
function resizeSsUpHandler(e) {
changeDiv();
divElement.style.visibility = 'visible';
fixDivElement.style.visibility = 'visible';
if (startCellFlag) {
startCellFlag = false;
}
if (startResizeSsFlag) {
startResizeSsFlag = false;
downScreenY = 0;
oldMoveScreenY = 0;
newMoveScreenY = 0;
SDivHeader = null;
document.removeEventListener('mousemove', resizeSsMoveHandler, false);
document.removeEventListener('mouseup', resizeSsUpHandler, false);
}
}
function resizeEsMoveHandler(e) {
if (startResizeEsFlag) {
newMoveScreenX = e.screenX;
var headerWidth = EDivHeader.getClientRects()[0].width;
var tableWidth = table.getClientRects()[0].width;
if (oldMoveScreenX == 0) {
oldMoveScreenX = downScreenX;
}
var changedEDiv = headerWidth + (newMoveScreenX - oldMoveScreenX);
EDivHeader.style.width = changedEDiv < 64 ? 64 + 'px' : changedEDiv + 'px';
table.style.width = changedEDiv < 64 ? tableWidth + 'px' : tableWidth + (newMoveScreenX - oldMoveScreenX) + 'px';
oldMoveScreenX = newMoveScreenX;
}
}
function resizeSsMoveHandler(e) {
if (startResizeSsFlag) {
newMoveScreenY = e.screenY;
var headerHeight = SDivHeader.getClientRects()[0].height;
var tableHeight = table.getClientRects()[0].height;
if (oldMoveScreenY == 0) {
oldMoveScreenY = downScreenY;
}
var changedSDiv = headerHeight + (newMoveScreenY - oldMoveScreenY);
SDivHeader.style.height = changedSDiv < 25 ? 25 + 'px' : changedSDiv + 'px';
table.style.height = changedSDiv < 25 ? tableHeight + 'px' : tableHeight + (newMoveScreenY - oldMoveScreenY) + 'px';
oldMoveScreenY = newMoveScreenY;
}
}
function hidden() {
addButton.style.visibility = 'hidden';
removeButton.style.visibility = 'hidden';
}
function headerMenuHandler(e) {
e.preventDefault();
}
function rowHeaderMenuHandler(e) {
e.preventDefault();
}
function headerUpHandler(e) {
hidden();
if (e.target.className == 'resizeE') {
return;
}
if (e.button == 2) {
headerChange = e.srcElement;
displayButton(headerChange);
}
}
function rowHeaderUpHandler(e) {
hidden();
if (e.target.className == 'resizeS') {
return;
}
if (e.button == 2) {
rowHeaderChange = e.srcElement;
displayButton(rowHeaderChange);
}
}
function displayButton(headChange) {
headChange.click();
addButton.style.visibility = 'visible';
removeButton.style.visibility = 'visible';
addButton.addEventListener('click', addHandler, false);
removeButton.addEventListener('click', removeHandler, false);
}
function addHandler(e) {
if (headerChange == null) { //插入行
var index = Number(rowHeaderChange.innerText);
var row = table.insertRow(index);
var rowHeader = row.insertCell(0);
appendRowHeader(rowHeader, index);
for (var i = 1; i <= headers.length; i++) {
appendCells(row, headers[i - 1].innerText, i);
}
for (var i = 0; i < rowHeaders.length; i++) {
if (Number(rowHeaders[i].children[0].innerText) >= index) {
rowHeaders[i].children[0].innerText = i + 1;
}
}
rowHeader.click();
rowHeaderChange = null;
} else { //插入列
var rows = table.rows;
var index = headerChange.innerText;
var clickHeader;
for (var i = 0; i < rows.length; i++) {
if (i == 0) { //插入th
clickHeader = appendTh(rows, index);
} else { //插入td
appendTd(rows, index, i);
}
}
clickHeader.click();
headerChange = null;
}
hidden();
}
function appendRowHeader(rowHeader, index) {
rowHeader.classList.add('rowHeader');
var resizeS = document.createElement('div');
var span = document.createElement('span');
resizeS.classList.add('resizeS');
rowHeader.appendChild(span);
rowHeader.appendChild(resizeS);
rowHeader.children[0].innerText = index;
resizeS.addEventListener('mousedown', resizeSsDownHandler, false);
rowHeader.addEventListener('click', rowHeaderHandler, false);
rowHeader.addEventListener('contextmenu', rowHeaderMenuHandler, false);
rowHeader.addEventListener('mouseup', rowHeaderUpHandler, false);
}
function appendCells(row, index, i) {
var cell = row.insertCell(i);
cell.classList.add('cell');
cell.setAttribute('data-index', index);
cell.addEventListener('click', cellHandler, false);
cell.addEventListener('dblclick', dblCellHandler, false);
cell.addEventListener('mousedown', cellDownHandler, false);
cell.addEventListener('mouseup', cellUpHandler, false);
cell.addEventListener('mousemove', cellMoveHandler, false);
}
function appendHeader(rows, index) {
var header = document.createElement('th');
header.classList.add('header');
var resizeE = document.createElement('div');
var span = document.createElement('span');
resizeE.classList.add('resizeE');
header.appendChild(span);
header.appendChild(resizeE);
header.children[0].innerText = index;
resizeE.addEventListener('mousedown', resizeEsDownHandler, false);
rows[0].appendChild(header);
header.addEventListener('click', headerHandler, false);
header.addEventListener('contextmenu', headerMenuHandler, false);
header.addEventListener('mouseup', headerUpHandler, false);
}
function appendTh(rows, index) {
var clickHeader;
var headChildren = rows[0].children;
var headLength = headChildren.length;
var firstInsert = false;
for (var j = 1; j <= headLength; j++) {
if (headChildren[j].innerText == index && !firstInsert) {
clickHeader = headChildren[j];
appendHeader(rows, index);
firstInsert = true;
} else {
if (firstInsert) {
headChildren[j].children[0].innerText = String.fromCharCode(headChildren[j - 1].children[0].innerText.charCodeAt() + 1);
}
}
}
return clickHeader;
}
function appendTd(rows, index, i) {
var rowsChildren = rows[i].children;
var rowLength = rowsChildren.length;
var firstInsert = false;
for (var j = 1; j <= rowLength; j++) {
if (rowsChildren[j].getAttribute('data-index') == index && !firstInsert) {
appendCells(rows[i], index, j);
firstInsert = true;
} else {
if (firstInsert) {
rowsChildren[j].setAttribute('data-index', String.fromCharCode(rowsChildren[j - 1].getAttribute('data-index').charCodeAt() + 1));
}
}
}
}
function removeHandler(e) {
if (headerChange == null) {//删除行
var index = rowHeaderChange.innerText;
var deleteHeight = rowHeaderChange.tagName == 'SPAN' ? rowHeaderChange.parentElement.getBoundingClientRect().height : rowHeaderChange.getBoundingClientRect().height;
table.style.height = table.getBoundingClientRect().height - deleteHeight + 'px';
table.deleteRow(index);
var rowHeader = removeRowHeader(Number(index));
rowHeader.click();
rowHeaderChange = null;
} else {//删除列
var rows = table.rows;
var index = headerChange.innerText;
var deleteWidth = headerChange.tagName == 'SPAN' ? headerChange.parentElement.getBoundingClientRect().width : headerChange.getBoundingClientRect().width;
table.style.width = table.getBoundingClientRect().width - deleteWidth + 'px';
var clickHeader;
for (var i = 0; i < rows.length; i++) {
if (i == 0) {//删除th
clickHeader = removeTh(rows, index);
} else {//删除td
removeTd(rows, index, i);
}
}
clickHeader.click();
headerChange = null;
}
hidden();
}
function removeRowHeader(index) {
var rowHeader;
for (var i = 0; i < rowHeaders.length; i++) {
if (rowHeaders[i].children[0].innerText > index) {
if (Number(rowHeaders[i].children[0].innerText) - 1 == index) {
rowHeader = rowHeaders[i];
}
rowHeaders[i].children[0].innerText = i + 1;
}
}
return rowHeader;
}
function removeTh(rows, index) {
var clickHeader;
var headChildren = rows[0].children;
var headLength = headChildren.length - 1;
for (var j = 1; j < headLength; j++) {
if (headChildren[j].innerText == index) {
clickHeader = headChildren[j + 1];
rows[0].removeChild(headChildren[j]);
}
if (headChildren[j].innerText > index) {
headChildren[j].children[0].innerText = String.fromCharCode(headChildren[j - 1].children[0].innerText.charCodeAt() + 1);
}
}
return clickHeader;
}
function removeTd(rows, index, i) {
var rowsChildren = rows[i].children;
var rowLength = rowsChildren.length - 1;
for (var j = 1; j < rowLength; j++) {
if (rowsChildren[j].getAttribute('data-index') == index) {
rows[i].deleteCell(j);
}
if (rowsChildren[j].getAttribute('data-index') > index) {
rowsChildren[j].setAttribute('data-index', String.fromCharCode(rowsChildren[j - 1].getAttribute('data-index').charCodeAt() + 1));
}
}
}
function cellHandler(e) {
hidden();
var index = e.target.getAttribute("data-index");
var rows = [e.srcElement, e.srcElement.parentElement];;
var rowHeader = rows[1].getElementsByClassName('rowHeader')[0];
setHeadsThree(index, rowHeader, rowHeaders);
setHeadsThree(index, rowHeader, headers);
if (both.classList.contains('active')) {
both.classList.remove('active');
}
divStatus[0] = rows[0];
divStatus[1] = rows[0];
changeDiv();
}
function headerHandler(e) {
if (e.target.className == 'resizeE') {
return;
}
hidden();
var event = e.currentTarget;
var clearBorderRight = false;
var index = event.innerText;
setHeadsOne(index, headers, clearBorderRight);
setHeadsTwo(clearBorderRight, rowHeaders, 'borderRight');
if (both.classList.contains('active')) {
both.classList.remove('active');
}
var rows = table.rows;
divStatus[0] = getDivStatus((rows)[1].children, index);
divStatus[1] = getDivStatus((rows)[rows.length - 1].children, index);
changeDiv();
}
function getDivStatus(row, index) {
for (var i = 1; i < row.length; i++) {
if (row[i].getAttribute("data-index") == index) {
return row[i];
}
}
}
function rowHeaderHandler(e) {
if (e.target.className == 'resizeS') {
return;
}
hidden();
var clearBorderBottom = false;
var index = e.target.innerText;
setHeadsOne(index, rowHeaders, clearBorderBottom);
setHeadsTwo(clearBorderBottom, headers, 'borderBottom');
if (both.classList.contains('active')) {
both.classList.remove('active');
}
var targetRow = e.target.tagName == 'SPAN' ? e.srcElement.parentElement.parentElement.children : e.srcElement.parentElement.children;
divStatus[0] = targetRow[1];
divStatus[1] = targetRow[targetRow.length - 1];
changeDiv();
}
//设置大框框相邻的头顶的元素样式
function setHeadsOne(index, heads, clearBorderName) {
for (var i = 0; i < heads.length; i++) {
//每次都需要判断删除,有没有什么其他妙招???
if (heads[i].classList.contains('active')) {
heads[i].classList.remove('active');
}
if (heads[i].innerText == index) {
if (heads[i].innerText == 1 || heads[i].innerText == 'A') {
clearBorderName = true;
}
heads[i].classList.add('active');
} else {
}
heads[i].classList.remove('selected');
}
}
//设置与大框框平行的最边缘的一排元素的样式
function setHeadsTwo(clearBorderBottom, heads, borderName) {
for (var i = 0; i < heads.length; i++) {
heads[i].classList.add('selected');
if (heads[i].classList.contains('active')) {
heads[i].classList.remove('active');
}
if (clearBorderBottom) {
heads[i].classList.remove('selected');
} else {
heads[i].classList.add('selected');
}
}
}
//设置与大框框两边平行的最边缘的单个样式属性
function setHeadsThree(index, rowHeader, heads) {
for (var i = 0; i < heads.length; i++) {
if (heads[i].classList.contains('active')) {
heads[i].classList.remove('active');
}
if (heads[i] == rowHeader || heads[i].innerText == index) {
heads[i].classList.add('selected');
} else {
if (heads[i].classList.contains('selected')) {
heads[i].classList.remove('selected');
}
}
}
}
function bothHandler(e) {
hidden();
divStatus[0] = cells[0];
var rows = table.rows;
var lastRow = rows[rows.length - 1].children;
divStatus[1] = lastRow[lastRow.length - 1];
changeDiv();
for (var i = 0; i < headers.length; i++) {
headers[i].classList.add('active');
}
for (var i = 0; i < rowHeaders.length; i++) {
rowHeaders[i].classList.add('active');
}
both.classList.add('active');
}
function dblCellHandler(e) {
hidden();
var rows = [e.srcElement, e.srcElement.parentElement];
fixDivElement.style.top = e.srcElement.getBoundingClientRect().top + 1.5 + 'px';
fixDivElement.style.left = e.srcElement.getBoundingClientRect().left + 1.5 + 'px';
var rect = e.srcElement.getBoundingClientRect();
var inputElement = document.createElement("input");
setTimeout(() => {
appendInput(inputElement, rows, rect);
}, 0);
var cell = e.srcElement;
var oldText = cell.innerText;
if (oldText != null) {
inputElement.value = oldText;
}
var rowHeader = rows[1].getElementsByClassName('rowHeader')[0];
var cellNext = getCellNext(rowHeader, cell);
inputElement.addEventListener('keyup', function (event) {
if (event.keyCode == 13) {
cellText(cell, inputElement.value);
//手动触发下一个cell的点击事件
cellNext.click();
inputElement.blur();
}
}, false);
inputElement.addEventListener('blur', function () {
cellText(cell, inputElement.value);
table.removeChild(inputElement);
}, false);
}
function appendInput(inputElement, rows, rect) {
inputElement.style.width = rows[0].getClientRects()[0].width + 'px';
inputElement.style.height = rows[0].getClientRects()[0].height + 'px';
inputElement.style.border = '1.5px solid rgb(21, 104, 10)';
inputElement.style.position = 'fixed';
inputElement.style.top = rect.top + 'px';
inputElement.style.left = rect.left + 'px';
inputElement.style.outline = 'none';
inputElement.style.zIndex = '3';
table.appendChild(inputElement);
inputElement.focus();
}
function getCellNext(rowHeader, cell) {
for (var i = 0; i < rowHeaders.length; i++) {
if (rowHeaders[i].innerText - 1 == rowHeader.innerText) {
var parent = rowHeaders[i].parentElement;
var children = parent.children;
for (var i = 0; i < children.length; i++) {
if (children[i].getAttribute("data-index") == cell.getAttribute("data-index")) {
return children[i];
}
}
}
}
}
function cellText(cell, text) {
cell.innerText = text;
if (isNaN(text)) {
cell.style.textAlign = 'left';
} else {
cell.style.textAlign = 'right';
}
}
function drawRect() {
var rowHeaderBorder = true;
var headerBorder = true;
var rowHeader = divStatus[0].parentElement.children[0];
if (rowHeader.innerText == 1) {
headerBorder = false;
}
if (divStatus[0].getAttribute("data-index") == 'A') {
rowHeaderBorder = false;
}
var startLeft = divStatus[0].getBoundingClientRect().left;
var startTop = divStatus[0].getBoundingClientRect().top;
var endLeft = divStatus[1].getBoundingClientRect().left;
var endTop = divStatus[1].getBoundingClientRect().top;
if (endTop <= startTop) {//上
moveChangeRowHeader(startTop, endTop, rowHeaderBorder);
}
if (endTop > startTop) {//下
moveChangeRowHeader(endTop, startTop, rowHeaderBorder);
}
if (endLeft <= startLeft) {//左
moveChangeHeader(startLeft, endLeft, headerBorder);
}
if (endLeft > startLeft) {//右
moveChangeHeader(endLeft, startLeft, headerBorder);
}
changeDiv();
divElement.style.backgroundColor = 'rgba(83, 83, 83, 0.3)';
}
function moveChangeRowHeader(startTop, endTop, rowHeaderBorder) {
for (var j = 0; j < rowHeaders.length; j++) {
var rowHeaderTop = rowHeaders[j].getBoundingClientRect().top;
if (rowHeaders[j].classList.contains('active')) {
rowHeaders[j].classList.remove('active');
}
if (rowHeaders[j].classList.contains('selected')) {
rowHeaders[j].classList.remove('selected');
}
if (rowHeaderTop <= startTop && rowHeaderTop >= endTop) {
rowHeaders[j].classList.add('selected');
if (rowHeaderBorder) {
rowHeaders[j].classList.add('selected');
} else {
rowHeaders[j].classList.remove('selected');
}
} else {
rowHeaders[j].classList.remove('selected');
}
}
}
function moveChangeHeader(startLeft, endLeft, headerBorder) {
for (var j = 0; j < headers.length; j++) {
var headerLeft = headers[j].getBoundingClientRect().left;
if (headers[j].classList.contains('active')) {
headers[j].classList.remove('active');
}
if (headers[j].classList.contains('selected')) {
headers[j].classList.remove('selected');
}
if (headerLeft >= endLeft && headerLeft <= startLeft) {
headers[j].classList.add('selected');
if (headerBorder) {
headers[j].classList.add('selected');
} else {
headers[j].classList.remove('selected');
}
} else {
headers[j].classList.remove('selected');
}
}
}
function cellDownHandler(e) {
hidden();
fixDivText(e.target);
divStatus[0] = e.target;
startCellFlag = true;
}
function cellUpHandler(e) {
startCellFlag = false;
}
function fixDivText(cell) {
setTimeout(() => {
fixDivElement.innerText = null;
var text = cell.innerText;
fixDivElement.innerText = text;
fixDivElement.style.lineHeight = fixDivElement.getBoundingClientRect().height + 'px';
if (isNaN(text)) {
fixDivElement.style.textAlign = 'left';
} else {
fixDivElement.style.textAlign = 'right';
}
}, 200);
}
function cellMoveHandler(e) {
if (startCellFlag) {
var rect = e.srcElement.getBoundingClientRect();
var cell = getEndCell(rect.left, rect.right, rect.top, rect.bottom);
divStatus[1] = cell;
drawRect();
}
}
function getEndCell(left, right, top, bottom) {
for (var i = 0; i < cells.length; i++) {
if (cells[i].getBoundingClientRect().left >= left && cells[i].getBoundingClientRect().top >= top
&& cells[i].getBoundingClientRect().right <= right && cells[i].getBoundingClientRect().bottom <= bottom) {
return cells[i];
}
}
return null;
}