仿Excel表格

首先可以點擊右邊的連接來體驗一下最終的效果: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章