纯原生javascript实现table表格的增删

公司实习生问我table的增删操作,用jQuery很简单的实现了。又问我不使用jQuery,只使用js如何实现。

面对这种情况,我的一贯做法是‘不理解,但是支持’。

jQuery用的多了,人也懒了,但还是用js实现了这一操作,觉得难点在于IE兼容。。。

只是想找代码看看的可以跳过分析过程,文章底部附有完整代码。

以下是coding过程:


HTML结构代码

一个基本的table结构,增加了一些简单的样式,三个按钮分别对应创建、清空,和一个预留。

<!DOCTYPE HTML>
<html>
    <head>
        <title>table</title>
        <meta charset='utf-8' />
        <style type="text/css">  
            table.base{  
                border-collapse:collapse;  
                text-align: center;  
                border: 1px solid black;
            }   
            table, tr, td, th{
                border: 1px solid black;
            }            
        </style>  
    </head>
    <body>
        <div id="main-content">
            <table id="main-table" class="base">
                <thead>
                    <tr>
                        <th colspan="3">This is a table for operations by javascript</th>
                    </tr>
                    <tr>
                        <th>
                            <input type="button" value="CREATE" id="cp_btn" οnclick="createTr()" />
                        </th>
                        <th>
                            <input type="button" value="CLEAR" id="cl_btn" οnclick="clearTrs()" />
                        </th>
                        <th>
                            <input type="button" value="GUESS" id="cl_btn"/>
                        </th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
    </body>
</html>


构造函数(伪构造函数)

考虑过,创建一个隐藏的tr,基于此tr执行创建操作。为了不破坏HTML整体结构,决定通过js生成tr对象并append到页面中。

为了在页面加载完成后,再执行dom操作,所以将<script>放在代码下端</body>之前。

基于table中的tbody进行增删操作,可以先声明此全局变量

var vTbody = document.getElementById('main-table').getElementsByTagName('tbody')[0];


创建对象,可以使用document.createElement方法。

以面向对象的方式进行编程,先写构造函数(其实并不是标准的构造函数格式),从最内部的元素开始。

td中可能会有text和button等表单元素,所以先创建一个 input 的构造函数function myInput(vId, vClass, vType, vValue, vParent){}

这里有一个兼容性问题,就是IE内核不支持setAttribute(class, value),需要使用setAttribute(className, value),所以为了解决兼容问题,可以通过

setAttribute(class, value) for FF、Chrome..

setAttribute(className, value) for IE 

这里采用的是另一种方式 .className,代码如下:

function myInput(vId, vClass, vType, vValue, vParent) {
	var vInput = document.createElement('input');
	if(vId) {
		vInput.setAttribute('id', vId);
	}
	vInput.setAttribute('type', vType);
	vInput.setAttribute('value', vValue);
	vInput.className = vClass;
	if(vParent) {
		vParent.appendChild(vInput);
	}
}


然后是td对象和tr对象的构造函数,大同小异,代码如下

function myTd(vId, vClass, vChild, vParent) {
    var vTd = document.createElement('td');
    if(vId){
        vTd.setAttribute('id', vId);
    }
    vTd.className = vClass;
    if(vChild) {
        vTd.appendChild(vChild);
    }
    if(vParent) {
        vParent.appendChild(vTd);
    }
    return vTd;
}


function myTr(vId, vClass, vChild, vParent) {
    var vTr = document.createElement('tr');
    if(vId){
        vTr.setAttribute('id', vId);
    }
    vTr.className = vClass;
    if(vChild) {
        vTr.appendChild(vChild);
    }
    if(vParent) {
        vParent.appendChild(vTr);
    }
    return vTr;
}


新建行方法createTr()

构造函数完成之后,完善createTr()方法。

预想的tr结构为 序号,文本框,操作按钮。

依次创建相关对象。序号列需要动态刷新,所以先设定class名称,通过方法执行排序操作。

function createTr() {
    var vTr = new myTr(null, null, null, vTbody);
    //序列td
    var vTdSeq = new myTd(null, 'seq', null, vTr);

    //文本框td
    var vTdText = new myTd(null, null, null, vTr);
    var vInputText = new myInput(null, 'td-inp-txt', 'text', '', vTdText);

    //操作按钮td
    var vTdBtn = new myTd(null, null, null, vTr);
    var vInputBtnCp = new myInput(null, 'td-inp-btn-cp', 'button', 'COPY', vTdBtn);
    var vInputBtnDel = new myInput(null, 'td-inp-btn-del', 'button', 'DELETE', vTdBtn);
}


排序方法reSequence()

创建一个动态排序方法reSequence() ,有一个兼容性问题 innerText在火狐下无效果,所以使用innerHTML。代码如下

function reSequence() {
    var vObj = vTbody.getElementsByClassName('seq');
    for (var i=0, len=vObj.length; i<len; i++) { 
        vObj[i].innerHTML = i+1;
    }
}


有一个兼容性问题,IE8及以下不支持getElementsByClassName()方法,网上找到了解决方案

if(!document.getElementsByClassName){
    document.getElementsByClassName = function(className, element){
        var children = (element || document).getElementsByTagName('*');
        var elements = new Array();
        for (var i=0; i<children.length; i++){
            var child = children[i];
            var classNames = child.className.split(' ');
            for (var j=0; j<classNames.length; j++){
                if (classNames[j] == className){ 
                    elements.push(child);
                    break;
                }
            }
        } 
        return elements;
    };
}


试图在Object或者是HTMLTableSectionElement的原型上增加此方法,如

HTMLTableSectionElement.prototype.getElementsByClassName = function(){}


可惜没有实现。

修改后的代码为

function reSequence() {
    var vObj = vTbody.getElementsByClassName == null?document.getElementsByClassName('seq', vTbody):vTbody.getElementsByClassName('seq');
    for (var i=0, len=vObj.length; i<len; i++) { 
        vObj[i].innerHTML = i+1;
    }
}

除了排序外,还需其他操作,所以我们创建一个init()方法,集中管理reSequence()这些方法,在createTr()方法的结尾调用init()方法。

清空行方法clearTrs()

移除/销毁某个dom对象,首先想到的是remove()方法,不幸的是,存在IE浏览器兼容问题,因此,采用了一个更简便的方式,对dom对象执行innerHTML="",代码如下

function clearTrs() {
    vTbody.innerHTML = '';
}
IE8报错,'未知的运行错误'。查了以下,因为ie8的table.innerHTML是只读属性,妹的!再改:

function clearTrs() {
    while(vTbody.rows.length >0) {
        vTbody.deleteRow();
    }
}

删除行方法addBtnDelsListener()

接下来,给DELETE按钮绑定删除当前行的方法。

为了解决兼容性问题,网上给出的方法是针对不同浏览器(IE、非IE)分别使用addEventListener、attachEvent方法,

我采用的是另一种解决方案:

obj.onclick = function(){};

匿名函数的方法体,吸取了上面clearTrs()方法的经验教训,直接采用

deleteRow(index)方法。

有一点需要注意thisTr.rowIndex获取的行数,比当前行要大2,因为thead中还有两行。所以当前的索引数=thisTr.rowIndex-vThead.rows.length

代码如下:

function addBtnDelsListener() {
    var vBtnDels = vTbody.getElementsByClassName == null?document.getElementsByClassName('td-inp-btn-del', vTbody):vTbody.getElementsByClassName('td-inp-btn-del');
    for (var i=0, len=vBtnDels.length; i<len; i++) {
        vBtnDels[i].onclick = function() {
            var vTr = this.parentElement.parentElement;
            vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
            reSequence();
        };
    }
}
执行完删除操作后,通过reSequence()方法重新排序。

同时将addBtnDelsListener()方法加入到init()方法中。

复制行方法addBtnCpsListener()

再来看一下COPY按钮,添加事件监听的方式同上。

如果innerHTML不是只读的话,可以createElement一个tr元素 然后newTr.innerHTML=thisTr.innerHTML,

为了兼容性,必须做些改变。

其实可以将复制看做是新建,唯一的不同在于新建行的文本输入框的内容要等同于被复制行。

这就简单了。我可以先调用createTr()方法,再将最后一个元素lastChild中的文本框的value等于被复制行。

思路有了,代码如下:

function addBtnCpsListener() {
    var vBtnCps = vTbody.getElementsByClassName == null?document.getElementsByClassName('td-inp-btn-cp', vTbody):vTbody.getElementsByClassName('td-inp-btn-cp');
    for (var i=0, len=vBtnCps.length; i<len; i++) {
        vBtnCps[i].onclick = function() {
            createTr();
            var vNewTr = vTbody.lastChild;
            var vTr = this.parentElement.parentElement;
            vNewTr.getElementsByClassName == null?document.getElementsByClassName('td-inp-txt', vNewTr)[0].value = document.getElementsByClassName('td-inp-txt', vTr)[0].value:vNewTr.getElementsByClassName('td-inp-txt')[0].value = vTr.getElementsByClassName('td-inp-txt')[0].value;
        }
    }
}

优化修改

进行一些优化修改工作:
var elements = new Array();
修改为:var elements = [];
原因:数组用[]更好

将addBtnDelsListener方法中的vBtnDels[i].onclick = function() {
修改为:vBtnDels[i].onclick = delTr;
外部新创建一个函数
	 function delTr() {
                var vTr = this.parentElement.parentElement;
                vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
                reSequence();
            }
原因:Don't make functions within a loop.

同理,将addBtnCpsListener中的vBtnCps[i].onclick = function() {
修改为:vBtnCps[i].onclick = copyTr;
外部新创建一个函数
            function copyTr() {
                createTr();
                var vNewTr = vTbody.lastChild;
                var vTr = this.parentElement.parentElement;
                vNewTr.getElementsByClassName === null?
                document.getElementsByClassName('td-inp-txt', vNewTr)[0].value = 
                document.getElementsByClassName('td-inp-txt', vTr)[0].value:
                vNewTr.getElementsByClassName('td-inp-txt')[0].value = 
                vTr.getElementsByClassName('td-inp-txt')[0].value;
            }


将copyTr()方法中的?:格式修改为if else函数。
修改为:
            function copyTr() {
                createTr();
                var vNewTr = vTbody.lastChild;
                var vTr = this.parentElement.parentElement;
                if(vNewTr.getElementsByClassName) {
                    vNewTr.getElementsByClassName('td-inp-txt')[0].value = 
                    vTr.getElementsByClassName('td-inp-txt')[0].value;
                } else {
                    document.getElementsByClassName('td-inp-txt', vNewTr)[0].value = 
                    document.getElementsByClassName('td-inp-txt', vTr)[0].value;
                }
            }
原因:?:预期返回值应该是一个变量or函数,而不应该是一个表达式操作。

有一点需要注意:js最佳实现经常看到要使用===替换==。但是本示例中的==null,如果替换成===null会在ie8一下版本中出现问题。

完整代码

至此,一个完全基于原生javascript,并且兼容至IE6的table增删完成了。

还是想吐槽一下,如果不兼容IE10以下的版本,可以节省50%的代码。如果使用jQuery,又可以节省50%的代码。对于实用主义的我而言,这一过程备受煎熬。不过还是从中有所收益的(违心。。)

以下为完整代码:

<!DOCTYPE HTML>
<html>
    <head>
        <title>table</title>
        <meta charset='utf-8' />
        <style type="text/css">  
            table.base{  
                border-collapse:collapse;  
                text-align: center;  
                border: 1px solid black;
            }   
            table, tr, td, th{
                border: 1px solid black;
            }            
        </style>  
    </head>
    <body>
        <div id="main-content">
            <table id="main-table" class="base">
                <thead>
                    <tr>
                        <th colspan="3">This is a table for operations by javascript</th>
                    </tr>
                    <tr>
                        <th>
                            <input type="button" value="CREATE" id="cp_btn" οnclick="createTr()" />
                        </th>
                        <th>
                            <input type="button" value="CLEAR" id="cl_btn" οnclick="clearTrs()" />
                        </th>
                        <th>
                            <input type="button" value="GUESS" id="cl_btn"/>
                        </th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
        <script type="text/javascript">
            if(!document.getElementsByClassName){
                document.getElementsByClassName = function(className, element){
                    var children = (element || document).getElementsByTagName('*');
                    var elements = [];
                    for (var i=0; i<children.length; i++){
                        var child = children[i];
                        var classNames = child.className.split(' ');
                        for (var j=0; j<classNames.length; j++){
                            if (classNames[j] == className){ 
                                elements.push(child);
                                break;
                            }
                        }
                    } 
                    return elements;
                };
            }
            var vTbody = document.getElementById('main-table').getElementsByTagName('tbody')[0];

            function myInput(vId, vClass, vType, vValue, vParent) {
                var vInput = document.createElement('input');
                if(vId) {
                    vInput.setAttribute('id', vId);
                }
                vInput.setAttribute('type', vType);
                vInput.setAttribute('value', vValue);
                vInput.className = vClass;
                if(vParent) {
                    vParent.appendChild(vInput);
                }
                return vInput;
            }

            function myTd(vId, vClass, vChild, vParent) {
                var vTd = document.createElement('td');
                if(vId){
                    vTd.setAttribute('id', vId);
                }
                vTd.className = vClass;
                if(vChild) {
                    vTd.appendChild(vChild);
                }
                if(vParent) {
                    vParent.appendChild(vTd);
                }
                return vTd;
            }

            function myTr(vId, vClass, vChild, vParent) {
                var vTr = document.createElement('tr');
                if(vId){
                    vTr.setAttribute('id', vId);
                }
                vTr.className = vClass;
                if(vChild) {
                    vTr.appendChild(vChild);
                }
                if(vParent) {
                    vParent.appendChild(vTr);
                }
                return vTr;
            }

            function createTr() {
                var vTr = new myTr(null, null, null, vTbody);
                //序列td
                var vTdSeq = new myTd(null, 'seq', null, vTr);

                //文本框td
                var vTdText = new myTd(null, null, null, vTr);
                var vInputText = new myInput(null, 'td-inp-txt', 'text', '', vTdText);

                //操作按钮td
                var vTdBtn = new myTd(null, null, null, vTr);
                var vInputBtnCp = new myInput(null, 'td-inp-btn-cp', 'button', 'COPY', vTdBtn);
                var vInputBtnDel = new myInput(null, 'td-inp-btn-del', 'button', 'DELETE', vTdBtn);

                init();
            }

            function clearTrs() {
                while(vTbody.rows.length >0) {
                    vTbody.deleteRow();
                }
            }

            function init(){
                reSequence();
                addBtnDelsListener();
                addBtnCpsListener();
            }
            function reSequence() {
                var vObj = vTbody.getElementsByClassName == null?
                            document.getElementsByClassName('seq', vTbody):
                            vTbody.getElementsByClassName('seq');
                for (var i=0, len=vObj.length; i<len; i++) { 
                    vObj[i].innerHTML = i+1;
                }
            }

            function addBtnDelsListener() {
                var vBtnDels = vTbody.getElementsByClassName == null?
                                document.getElementsByClassName('td-inp-btn-del', vTbody):
                                vTbody.getElementsByClassName('td-inp-btn-del');
                for (var i=0, len=vBtnDels.length; i<len; i++) {
                    vBtnDels[i].onclick = delTr;
                }
            }
            function delTr() {
                var vTr = this.parentElement.parentElement;
                vTbody.deleteRow(vTr.rowIndex-vTbody.parentNode.getElementsByTagName('thead')[0].rows.length);
                reSequence();
            }

            function addBtnCpsListener() {
                var vBtnCps = vTbody.getElementsByClassNamenull == null?
                                document.getElementsByClassName('td-inp-btn-cp', vTbody):
                                vTbody.getElementsByClassName('td-inp-btn-cp');
                for (var i=0, len=vBtnCps.length; i<len; i++) {
                    vBtnCps[i].onclick = copyTr;
                }
            }

            function copyTr() {
                createTr();
                var vNewTr = vTbody.lastChild;
                var vTr = this.parentElement.parentElement;
                if(vNewTr.getElementsByClassName) {
                    vNewTr.getElementsByClassName('td-inp-txt')[0].value = 
                    vTr.getElementsByClassName('td-inp-txt')[0].value;
                } else {
                    document.getElementsByClassName('td-inp-txt', vNewTr)[0].value = 
                    document.getElementsByClassName('td-inp-txt', vTr)[0].value;
                }
            }
        </script>
    </body>
</html>



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 HTML 表格宽度拖拽,可以使用原生 JavaScript实现。以下是一个简单的实现方式: HTML 代码: ``` <table id="myTable"> <tr> <th>列1</th> <th>列2</th> <th>列3</th> </tr> <tr> <td>内容1</td> <td>内容2</td> <td>内容3</td> </tr> <tr> <td>内容4</td> <td>内容5</td> <td>内容6</td> </tr> </table> ``` JavaScript 代码: ``` var table = document.getElementById("myTable"); var isResizing = false; var lastDownX = 0; // 设置每列的初始宽度 var columnWidths = [100, 100, 100]; for (var i = 0; i < table.rows[0].cells.length; i++) { table.rows[0].cells[i].style.width = columnWidths[i] + "px"; } // 鼠标按下时记录位置 table.addEventListener("mousedown", function (e) { isResizing = true; lastDownX = e.clientX; }); // 鼠标移动时改变列宽度 table.addEventListener("mousemove", function (e) { if (!isResizing) { return; } var cell = e.target.parentElement; var cellIndex = Array.from(cell.parentElement.children).indexOf(cell); var widthDiff = e.clientX - lastDownX; // 改变当前列的宽度 var newWidth = columnWidths[cellIndex] + widthDiff; if (newWidth > 0) { cell.style.width = newWidth + "px"; columnWidths[cellIndex] = newWidth; lastDownX = e.clientX; } }); // 鼠标抬起时停止改变列宽度 table.addEventListener("mouseup", function (e) { isResizing = false; }); ``` 以上代码会在表格中添加一个鼠标事件监听器,当用户按下鼠标并移动时,会根据鼠标的位置改变被选中列的宽度。需要注意的是,当用户拖动列宽度时,应该记录每列的初始宽度(在这里使用了 `columnWidths` 数组来记录),并在移动过程中修改当前列的宽度,而不是仅仅修改当前列的宽度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值