zoukankan      html  css  js  c++  java
  • 【CSON原创】 支持行拖动,列拖动的表格插件

    效果预览:
    支持行拖动,列拖动的表格插件
    col1_headcol2_headcol3_headcol4_head
    1 2 3 4
    2 3 4 5
    3 4 5 6
    4 5 6 7
    4 6 7 8
     
    功能说明:

    当鼠标移动到表头区域时可以对列进行拖动排序,鼠标移动到行区域时可以对行进行拖动排序。

    支持IE6 7 8 firefox chrome

    实现原理:

    当鼠标在表头区域按下时,复制现有的table(不复制其后代结点),并把选择列的所有元素复制添加到新table中,通过按下时的鼠标位置和鼠标移动坐标,确定新table的位置,在行区域按下时同理。

    代码分析:

    var SortTable = (function() {

    returnfunction(sTableId, options) {
    this.init(sTableId, options);
    }


    })();

    首先在构造函数里调用sortTable的init方法进行初始化。需要传入table的id和自定义选项对象,该对象里面包含用户自定义的属性值,对于没有传入的值,使用默认值。

    SortTable.prototype = (function() {

    var dragTable; //保存拖动的table
    var isDragCol =false; //是否拖动列
    var isDragsRow =false; //是否拖动行
    var dragingIndex; //拖动的列索引或行索引


    /* 点击位置相对于元素位置的位置对象 */
    var relPos = {
    left:
    0,
    top:
    0,
    updatePos:
    function(left, top) { //更新目前的相对位置
    this.left = left;
    this.top = top;
    }

    };

        首先prototype里面有四个私有变量:

        1.拖动中的table的引用:就是拖动行或者拖动列对象的引用

        2.是否拖动列的布尔值:用户mouseover事件处理程序中判断是否处于拖动列的状态中,

        3.是否拖动行的布尔值:用户mouseover事件处理程序中判断是否处于拖动列的状态中,

        4.拖动行/列的索引:就是拖动的行或者列的索引值,用于查找该行/列并复制成新的table对象。

    /* 设定索引和左位置值 */
    var setIndexAndPos =function(elems, dir) {
    dir
    = dir ||'left';

    for (var i =0, len = elems.length; i < len; i++) {
    elems[i].index
    = i;
    if (dir =='left') {
    elems[i].posLeft
    = util.getCurrentPosition(elems[i]).left;
    }
    elseif (dir =='top') {
    elems[i].posTop
    = util.getCurrentPosition(elems[i]).top;
    }

    }

    };

           该私有方法在初始化过程中被调用,遍历元素集合,并设置其index和left或top的位置值。其中left值用于列拖动,top值用于行拖动,设定哪个值根据用户传入的dir来判断。

    /* 设置新结点的样式 */
    var setNewNodeStyle =function(newNode, nodeToClone) {

    var newStyle = newNode.style;
    var oldStyle = util.getComputedStyle(nodeToClone);
    newStyle.paddingLeft
    = oldStyle.paddingLeft;
    newStyle.paddingRight
    = oldStyle.paddingRight;
    newStyle.paddingTop
    = oldStyle.paddingTop;
    newStyle.paddingBottom
    = oldStyle.paddingBottom;
    newStyle.width
    = nodeToClone.clientWidth - parseInt(newStyle.paddingLeft) - parseInt(newStyle.paddingRight) +'px';
    newStyle.height
    = nodeToClone.clientHeight - parseInt(newStyle.paddingTop) - parseInt(newStyle.paddingBottom) +'px';

    }

          用于根据某结点设定新结点的样式,使新结点的样式和参考结点的样式相同。该私有方法将用于行拖动时使新table各个td的值和参考table中的一样,目的是使拖动行幻影的样式和表格中的行一致。

    /* 把点击列整列结点复制到新table */
    var copyCol =function(index, rows, dragingClass, oTable) {

    var newNode;
    var nodeToClone;
    var newTr;
    var newBody;

    dragTable
    = oTable.cloneNode(false);
    newBody
    = document.createElement('TBODY'); //ie下tr必须包在thead tbody或tfoot里
    dragTable.appendChild(newBody);

    dragTable.className
    +=''+ dragingClass;
    for (var i =0, len = rows.length; i < len; i++) {
    newTr
    = document.createElement('TR');

    nodeToClone
    = rows[i].cells[index];
    newNode
    = nodeToClone.cloneNode(true);
    newTr.appendChild(newNode);
    newBody.appendChild(newTr);
    }
    document.body.appendChild(dragTable);
    }

          之后的函数用于把所点击列的结点复制到新的table用作拖动幻影。需要注意的是对于IE,必须创建tbody元素,并把新table的tr包含在tbody里,否则新table的结点会显示不出来。另外新table需要添加拖动样式类,该类在初始化时由用户传入,决定拖动幻影的样式。

    /* 复制行 */
    var copyRow =function(oTr, dragingClass, oTable) {
    var newTd;
    var oldTd;
    var newBody;
    var dragTr;


    dragTable
    = oTable.cloneNode(false);
    newBody
    = document.createElement('TBODY');
    dragTable.appendChild(newBody);

    dragTr
    = oTr.cloneNode(true);
    newBody.appendChild(dragTr);


    dragTable.className
    +=''+ dragingClass;
    for (var i =0, len = dragTr.cells.length; i < len; i++) {
    newTd
    = dragTr.cells[i];
    oldTd
    = oTr.cells[i];
    setNewNodeStyle(newTd, oldTd);
    }

    document.body.appendChild(dragTable);

    }

          复制行的函数,方法和复制列大致一样,通过创建新table,并根据所拖动行的索引获取对应的tr,把该tr复制到新table中去,同样要记得创建tbody标签。

    /* 拖动时更新位置 */
    var updateDragingPosition =function(left, top) {
    dragTable.style.left
    = left +'px';
    dragTable.style.top
    = top +'px';
    }

          该方法在每次触发mousemove事件时调用,作用是更新拖动幻影的位置坐标,使之随着鼠标的移动而移动。

    /* 放置拖动列 */
    var placeCol =function(left, headers, rows) {

    var halfDistant;
    var parent = headers[0].parent;
    var len = headers.length;

    if (left < headers[0].posLeft) {
    placeAllColCells(
    0, rows);
    return;
    }
    else {
    for (var i =0; i < len; i++) {
    halfDistant
    = headers[i].posLeft + (headers[i].clientWidth /2);
    if (left >= headers[i].posLeft && left < halfDistant) {

    placeAllColCells(i, rows);

    return;
    }
    elseif (left >= halfDistant && left < headers[i].posLeft + headers[i].clientWidth) {
    placeAllColCells(i
    +1, rows);
    return;
    }
    }
    placeAllColCells(i, rows);
    }

    };

          该方法用于在每次鼠标松开时,把选定的列插入到目标位置。在确定位置之前会左三个判断:

         1.首先判断目前拖动幻影左位置是否小于第一个表头的左位置,是则把选定列插入到第一个列之前。

         2.逐个判断处于列的前半部分还是后半部分,如果是前半部分,则插入到该列的之前,否则插入到该列之后。

         3.如果之前的情况都不符合,则把结点插入到列集合的最尾。

    /* 放置拖动列的所有结点 */

    var placeAllColCells =function(index, rows) {


    for (var i =0, len = rows.length; i < len; i++) {
    var parent = rows[i].cells[0].parentNode;

    if (index == rows[i].cells.length) {
    parent.appendChild(rows[i].cells[dragingIndex]);
    }
    else {
    parent.insertBefore(rows[i].cells[dragingIndex], rows[i].cells[index]);
    }
    }


    }

          函数在上面的函数确定了插入列位置之后,使用该函数进行具体的插入操作,其中如果传入的i和length相等,则表示需要插入到列集合的最后,这时需要用appendchild而非insertbefore(虽然非IE的浏览器中insertbefore可以使用不存在的“尾结点”作为参考点来进行插入操作,但是IE会报错,所以为了兼容,还是应该使用appendchild进行插入)。

    /* 放置拖动行 */
    var placeRow =function(top, rows) {
    var halfDistant;
    var parent = rows[0].parentNode;
    var len = rows.length;

    if (top < rows[0].posTop) {
    parent.insertBefore(rows[dragingIndex], rows[
    0]);
    return;
    }
    else {

    for (var i =0; i < len; i++) {
    halfDistant
    = rows[i].posTop + (rows[i].clientHeight /2);
    if (top >= rows[i].posTop && top < halfDistant) {
    parent.insertBefore(rows[dragingIndex], rows[i]);
    return;
    }
    elseif (top >= halfDistant && top < rows[i].posTop + rows[i].clientHeight) {
    i
    +1< len ? parent.insertBefore(rows[dragingIndex], rows[i +1]) : parent.appendChild(rows[dragingIndex]);
    return;
    }
    }
    parent.appendChild(rows[dragingIndex]);
    }

    };

          放置拖动行的函数,该函数思路和放置拖动列的差不多,也是通过对三种情况的判断,确定行插入位置,再进行具体的插入操作,详细可以参考上面的列插入操作。

    /* 删除拖动幻影列 */
    var removeShadowArray =function() {

    document.body.removeChild(dragTable);

    };

          该函数用于在鼠标松开时,删除拖动幻影。

    /* 鼠标按下事件处理程序 */
    var downHandler =function(rows, dragingClass, oTable, onBeginDrag) {

    returnfunction(event) {
    var _elemPosition;
    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);
    var selectedTr;

    if (target.tagName !=='TH'&& target.tagName !=='TD'&& target.parentNode.tagName !=='TH') {
    return;
    }
    else {
    onBeginDrag();
    //执行开始拖动的回调函数

    if (target.tagName =='TH') { //判断点击是否为th(列拖动)
    dragingIndex = target.index;
    _elemPosition
    = util.getCurrentPosition(target);
    relPos.updatePos(event.clientX
    - _elemPosition.left, event.clientY - _elemPosition.top);
    copyCol(dragingIndex, rows, dragingClass, oTable);
    isDragCol
    =true;
    }

    elseif (target.tagName =='TD') { //判断点击是否为td(行拖动)

    selectedTr
    = target.parentNode;
    dragingIndex
    = selectedTr.index;
    _elemPosition
    = util.getCurrentPosition(selectedTr);
    relPos.updatePos(event.clientX
    - _elemPosition.left, event.clientY - _elemPosition.top);
    copyRow(selectedTr, dragingClass, oTable);
    isDragsRow
    =true;
    }

    updateDragingPosition(event.clientX
    - relPos.left, event.clientY - relPos.top); //更新拖动幻影位置
    }

    }

    };

          鼠标按下时的事件处理程序,其中的onBeginDrag是用户初始化时定义的开始拖动的回调函数。然后判断点击区域 ,再进行相应的行拖动或列拖动。最后调用updateDragingPosition函数更新拖动幻影的位置。

    /* 鼠标拖动事件处理程序 */
    var moveHandler =function(oTable, onDraging) {
    returnfunction(event) {

    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);

    if (isDragCol || isDragsRow) {
    onDraging();
    updateDragingPosition(event.clientX
    - relPos.left, event.clientY - relPos.top, oTable);

    }

    }

    };

          鼠标拖动的处理程序,其中如果是行拖动或者列拖动状态,则更新拖动幻影的坐标(行拖动和列拖动使用同一个更新函数)。并调用onDraging函数,该函数在初始化时由用户定义,在拖动期间执行。

    /* 鼠标松开事件处理程序 */
    var upHandler =function(headers, rows, tBodyRows, onEndDrag) {
    returnfunction(event) {

    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);
    if (!isDragCol &&!isDragsRow) {
    return;
    }
    else {
    if (isDragCol) {

    placeCol(event.clientX
    - relPos.left, headers, rows);
    isDragCol
    =false;
    setIndexAndPos(headers,
    'left');

    }
    elseif (isDragsRow) {
    placeRow(event.clientY
    - relPos.top, tBodyRows);
    isDragsRow
    =false;
    setIndexAndPos(tBodyRows,
    'top');

    }
    removeShadowArray();
    onEndDrag();

    }

    }

    }

            鼠标松开时的事件处理程序,把相应的拖动状态值设置为false,并调用removeShadowArray函数把拖动幻影的table从文档中删除。最后触发onEndDrag回调函数,该函数由用户在初始化时定义。

    /* 为事件添加处理程序 */
    var addDragHandler =function(rows, dragingClass, oTable, headers, tBodyRows, onBeginDrag, onEndDrag, onDraging) {
    util.addEventHandler(document,
    'mousedown', downHandler(rows, dragingClass, oTable, onBeginDrag));
    util.addEventHandler(document,
    'mousemove', moveHandler(oTable, onDraging));
    util.addEventHandler(document,
    'mouseup', upHandler(headers, rows, tBodyRows, onEndDrag));

    };

          为拖动事件添加事件处理程序。

    return {
    /* 初始化 */
    init:
    function(sTableId, options) {


    var defaults = {
    dragingClass:
    'draging',
    onBeginDrag: util.emptyFunction,
    onEndDrag: util.emptyFunction,
    onDraging: util.emptyFunction


    };
    util.extend(defaults, options);

    this.oTable = util.$(sTableId);
    this.rows =this.oTable.rows;
    this.tHead =this.oTable.tHead;
    this.tBody =this.oTable.tBodies[0];
    this.headers =this.tHead.rows[0].cells;
    this.dragingClass = defaults.dragingClass;
    this.onBeginDrag = util.bindFunction(defaults.onBeginDrag);
    this.onEndDrag = util.bindFunction(defaults.onEndDrag);
    this.onDraging = util.bindFunction(defaults.onDraging);

    setIndexAndPos(
    this.headers, 'left');
    setIndexAndPos(
    this.tBody.rows, 'top');

    addDragHandler(
    this.rows, this.dragingClass, this.oTable, this.headers, this.tBody.rows, this.onBeginDrag, this.onEndDrag, this.onDraging);



    }


    }

    })();

          最后返回prototype对象,其中的init方法用户初始化。另外需要注意的是三个回调函数由于使用了bindFunction,因此它们调用时this指向该SortTable对象,可以方便地使用该对象中的属性值。

    new SortTable('sTable');

          最后是调用方法。

    所有源代码:

    View Code
    var SortTable = (function() {



    returnfunction(sTableId, options) {
    this.init(sTableId, options);
    }


    })();

    SortTable.prototype
    = (function() {



    var dragTable; //保存拖动的table
    var isDragCol =false; //是否拖动列
    var isDragsRow =false;//是否拖动行
    var dragingIndex; //拖动的列索引或行索引


    /* 点击位置相对于元素位置的位置对象 */
    var relPos = {
    left:
    0,
    top:
    0,
    updatePos:
    function(left, top) {
    this.left = left;
    this.top = top;
    }

    };

    /* 设定索引和左位置值 */
    var setIndexAndPos =function(elems, dir) {
    dir
    = dir ||'left';

    for (var i =0, len = elems.length; i < len; i++) {
    elems[i].index
    = i;
    if (dir =='left') {
    elems[i].posLeft
    = util.getCurrentPosition(elems[i]).left;
    }
    elseif (dir =='top') {
    elems[i].posTop
    = util.getCurrentPosition(elems[i]).top;
    }

    }

    };
    /* 设置新结点的样式 */
    var setNewNodeStyle =function(newNode, nodeToClone) {

    var newStyle = newNode.style;
    var oldStyle = util.getComputedStyle(nodeToClone);
    newStyle.paddingLeft
    = oldStyle.paddingLeft;
    newStyle.paddingRight
    = oldStyle.paddingRight;
    newStyle.paddingTop
    = oldStyle.paddingTop;
    newStyle.paddingBottom
    = oldStyle.paddingBottom;
    newStyle.width
    = nodeToClone.clientWidth - parseInt(newStyle.paddingLeft) - parseInt(newStyle.paddingRight) +'px';
    newStyle.height
    = nodeToClone.clientHeight - parseInt(newStyle.paddingTop) - parseInt(newStyle.paddingBottom) +'px';

    }

    /* 把点击列整列结点复制到新table */
    var copyCol =function(index, rows, dragingClass, oTable) {

    var newNode;
    var nodeToClone;
    var newTr;
    var newBody;

    dragTable
    = oTable.cloneNode(false);
    newBody
    = document.createElement('TBODY'); //ie下tr必须包在thead tbody或tfoot里
    dragTable.appendChild(newBody);

    dragTable.className
    +=''+ dragingClass;
    for (var i =0, len = rows.length; i < len; i++) {
    newTr
    = document.createElement('TR');

    nodeToClone
    = rows[i].cells[index];
    newNode
    = nodeToClone.cloneNode(true);
    newTr.appendChild(newNode);
    newBody.appendChild(newTr);
    }
    document.body.appendChild(dragTable);
    }


    /* 复制行 */
    var copyRow =function(oTr, dragingClass, oTable) {
    var newTd;
    var oldTd;
    var newBody;
    var dragTr;


    dragTable
    = oTable.cloneNode(false);
    newBody
    = document.createElement('TBODY');
    dragTable.appendChild(newBody);

    dragTr
    = oTr.cloneNode(true);
    newBody.appendChild(dragTr);


    dragTable.className
    +=''+ dragingClass;
    for (var i =0, len = dragTr.cells.length; i < len; i++) {
    newTd
    = dragTr.cells[i];
    oldTd
    = oTr.cells[i];
    setNewNodeStyle(newTd, oldTd);
    }

    document.body.appendChild(dragTable);

    }
    /* 拖动时更新位置 */
    var updateDragingPosition =function(left, top) {

    dragTable.style.left
    = left +'px';
    dragTable.style.top
    = top +'px';

    }
    /* 放置拖动列的所有结点 */

    var placeAllColCells =function(index, rows) {


    for (var i =0, len = rows.length; i < len; i++) {
    var parent = rows[i].cells[0].parentNode;

    if (index == rows[i].cells.length) {
    parent.appendChild(rows[i].cells[dragingIndex]);
    }
    else {
    parent.insertBefore(rows[i].cells[dragingIndex], rows[i].cells[index]);
    }
    }


    }
    /* 放置拖动列 */
    var placeCol =function(left, headers, rows) {

    var halfDistant;
    var parent = headers[0].parent;
    var len = headers.length;

    if (left < headers[0].posLeft) {
    placeAllColCells(
    0, rows);
    return;
    }
    else {
    for (var i =0; i < len; i++) {
    halfDistant
    = headers[i].posLeft + (headers[i].clientWidth /2);
    if (left >= headers[i].posLeft && left < halfDistant) {

    placeAllColCells(i, rows);

    return;
    }
    elseif (left >= halfDistant && left < headers[i].posLeft + headers[i].clientWidth) {
    placeAllColCells(i
    +1, rows);
    return;
    }
    }
    placeAllColCells(i, rows);
    }

    };
    /* 放置拖动行 */
    var placeRow =function(top, rows) {
    var halfDistant;
    var parent = rows[0].parentNode;
    var len = rows.length;

    if (top < rows[0].posTop) {
    parent.insertBefore(rows[dragingIndex], rows[
    0]);
    return;
    }
    else {

    for (var i =0; i < len; i++) {
    halfDistant
    = rows[i].posTop + (rows[i].clientHeight /2);
    if (top >= rows[i].posTop && top < halfDistant) {
    parent.insertBefore(rows[dragingIndex], rows[i]);
    return;
    }
    elseif (top >= halfDistant && top < rows[i].posTop + rows[i].clientHeight) {
    i
    +1< len ? parent.insertBefore(rows[dragingIndex], rows[i +1]) : parent.appendChild(rows[dragingIndex]);
    return;
    }
    }
    parent.appendChild(rows[dragingIndex]);
    }

    };

    /* 删除拖动幻影列 */
    var removeShadowArray =function() {

    document.body.removeChild(dragTable);

    };

    /* 鼠标按下事件处理程序 */
    var downHandler =function(rows, dragingClass, oTable, onBeginDrag) {

    returnfunction(event) {
    var _elemPosition;
    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);
    var selectedTr;

    if (target.tagName !=='TH'&& target.tagName !=='TD'&& target.parentNode.tagName !=='TH') {
    return;
    }
    else {
    onBeginDrag();
    //执行开始拖动的回调函数

    if (target.tagName =='TH') { //判断点击是否为th(列拖动)
    dragingIndex = target.index;
    _elemPosition
    = util.getCurrentPosition(target);
    relPos.updatePos(event.clientX
    - _elemPosition.left, event.clientY - _elemPosition.top);
    copyCol(dragingIndex, rows, dragingClass, oTable);
    isDragCol
    =true;
    }

    elseif (target.tagName =='TD') { //判断点击是否为td(行拖动)

    selectedTr
    = target.parentNode;
    dragingIndex
    = selectedTr.index;
    _elemPosition
    = util.getCurrentPosition(selectedTr);
    relPos.updatePos(event.clientX
    - _elemPosition.left, event.clientY - _elemPosition.top);
    copyRow(selectedTr, dragingClass, oTable);
    isDragsRow
    =true;
    }

    updateDragingPosition(event.clientX
    - relPos.left, event.clientY - relPos.top); //更新拖动幻影位置
    }

    }

    };


    /* 鼠标拖动事件处理程序 */
    var moveHandler =function(oTable, onDraging) {
    returnfunction(event) {

    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);

    if (isDragCol || isDragsRow) {
    onDraging();
    updateDragingPosition(event.clientX
    - relPos.left, event.clientY - relPos.top, oTable);

    }

    }

    };



    /* 鼠标松开事件处理程序 */
    var upHandler =function(headers, rows, tBodyRows, onEndDrag) {
    returnfunction(event) {

    var event = util.getEventObj(event);
    var target = util.getEventTarget(event);
    if (!isDragCol &&!isDragsRow) {
    return;
    }
    else {
    if (isDragCol) {

    placeCol(event.clientX
    - relPos.left, headers, rows);
    isDragCol
    =false;
    setIndexAndPos(headers,
    'left');

    }
    elseif (isDragsRow) {
    placeRow(event.clientY
    - relPos.top, tBodyRows);
    isDragsRow
    =false;
    setIndexAndPos(tBodyRows,
    'top');

    }
    removeShadowArray();
    onEndDrag();

    }

    }

    }
    /* 为事件添加处理程序 */
    var addDragHandler =function(rows, dragingClass, oTable, headers, tBodyRows, onBeginDrag, onEndDrag, onDraging) {
    util.addEventHandler(document,
    'mousedown', downHandler(rows, dragingClass, oTable, onBeginDrag));
    util.addEventHandler(document,
    'mousemove', moveHandler(oTable, onDraging));
    util.addEventHandler(document,
    'mouseup', upHandler(headers, rows, tBodyRows, onEndDrag));

    };


    return {
    /* 初始化 */
    init:
    function(sTableId, options) {


    var defaults = {
    dragingClass:
    'draging',
    onBeginDrag: util.emptyFunction,
    onEndDrag: util.emptyFunction,
    onDraging: util.emptyFunction


    };
    util.extend(defaults, options);

    this.oTable = util.$(sTableId);
    this.rows =this.oTable.rows;
    this.tHead =this.oTable.tHead;
    this.tBody =this.oTable.tBodies[0];
    this.headers =this.tHead.rows[0].cells;
    this.dragingClass = defaults.dragingClass;
    this.onBeginDrag = util.bindFunction(defaults.onBeginDrag);
    this.onEndDrag = util.bindFunction(defaults.onEndDrag);
    this.onDraging = util.bindFunction(defaults.onDraging);

    setIndexAndPos(
    this.headers, 'left');
    setIndexAndPos(
    this.tBody.rows, 'top');

    addDragHandler(
    this.rows, this.dragingClass, this.oTable, this.headers, this.tBody.rows, this.onBeginDrag, this.onEndDrag, this.onDraging);



    }


    }

    })();


    欢迎转载,请标明出处:http://www.cnblogs.com/Cson/archive/2011/07/08/2101317.html

  • 相关阅读:
    华为AR2204多VLAN走不同wan口
    supervisord管理Django项目
    Django3 channels websocket实时读取日志返回前端
    雪球网接口测试
    算法图解: 1.二分查找
    HttpRunner3源码阅读:10.测试执行的处理 runner
    HttpRunner3源码阅读:9. 测试用例中的类定义testcase
    HttpRunner3源码阅读:8. 用例文件生成并格式化make
    HttpRunner3源码阅读:7.响应后处理 response.py
    HttpRunner3源码阅读:6.请求客户端client
  • 原文地址:https://www.cnblogs.com/Cson/p/2101317.html
Copyright © 2011-2022 走看看