javascript实现动态模态绑定grid过程代码


Posted in Javascript onSeptember 22, 2014
<html> 
<head> 
  <style type="text/css"> 
    .grid{border:1px solid #808080; border-spacing:0; width:500px; border-collapse:collapse} 
    .grid th,.grid td{border:0; text-align:center;} 
    .grid tr{height:25px;line-height:25px;} 
    .grid tr.odd{background:#d0d0d0} 
    .grid .btn{width:80px; text-align:center}     
  </style> 
   
  <script type="text/javascript"> 
    (function(){ 
      // 
      var util = { 
        next:function next(ele){ 
          if(ele){ 
            var n = ele.nextSibling; 
            if(n && n.nodeType === 1){ 
              return n; 
            } 
            return next(n); 
          } 
        }, 
        parseJSON:function(str){ 
          if(typeof JSON !== "undefined"){ 
            return JSON.parse(str); 
          } 
          return eval("("+str+")"); 
        }, 
        parseArray:function(obj){ 
          if(!obj){ 
            return obj; 
          } 
          var result = []; 
          if(typeof obj.length !== "undefined"){ 
            try{ 
              var arr = Array.prototype.slice.call(obj,0); 
              result = arr; 
            }catch(e){ 
              for(var i=0;i<obj.length;i++){ 
                try{ 
                  var target = obj[i]; 
                }catch(e){ 
                  if(obj.item){ 
                    var target = this.item(i);//nodeList 
                  } 
                } 
                if(typeof target !== "undefined"){ 
                  result.push(target); 
                  delete target; 
                } 
              } 
            } 
          } 
          return result; 
        }, 
        isFunction:function(fn){ 
          return typeof fn === "function"; 
        }, 
        trim:function(str){ 
          if(typeof str !== "string"){ 
            return str; 
          } 
          return str.replace(/^\s+|\s+$/g,""); 
        }, 
        offset:function offset(ele){ 
          var result = {top:0,left:0}; 
          if(!ele || ele.nodeType !== 1){ 
            return result; 
          } 
          result.top = Number(ele.offsetTop || (ele.style.top || "0").replace(/[^\d]+$/,"")); 
          result.left = Number(ele.offsetLeft || (ele.style.left || "0").replace(/[^\d]+$/,"")); 
          if(ele.parentNode){ 
            var r = offset(ele.parentNode); 
            result.top += r.top; 
            result.left += r.left; 
          } 
          return result; 
        } 
      }; 
       
      //事件处理 
      var Event = { 
        on:function(eventType,fn){ 
          var element = this; 
          if(this.addEventListener){ 
            this.addEventListener(eventType,fn,false); 
          }else if(this.attachEvent){ 
             //将事件缓冲到该标签上,已解决this指向window(现fn内this指向element)和移除匿名事件问题             
            var _EventRef='_'+eventType+'EventRef'; 
            if(!element[_EventRef]){ 
              element[_EventRef]=[]; 
            } 
            var _EventRefs=element[_EventRef]; 
            var index; 
            for(index in _EventRefs){ 
              if(_EventRefs[index]['realFn']==fn){ 
                return; 
              } 
            } 
            var nestFn=function(){ 
              fn.apply(element,arguments); 
            }; 
            element[_EventRef].push({'realFn':fn,'nestFn':nestFn}); 
            element.attachEvent('on'+eventType,nestFn); 
          }           
        }, 
        remove:function(eventType,fn){      
          var element = this;    
          if(this.removeEventListener){ 
            this.removeEventListener(eventType,fn,false); 
          }else if(this.detachEvent){ 
            var _EventRef='_'+eventType+'EventRef'; 
            if(!element[_EventRef]){ 
              element[_EventRef]=[]; 
            } 
            var _EventRefs=element[_EventRef] 
            var index; 
            var nestFn; 
            for(index in _EventRefs){ 
              if(_EventRefs[index]['realFn']==fn){ 
                nestFn=_EventRefs[index]['nestFn']; 
                if(index==_EventRefs.length-1){ 
                  element[_EventRef]=_EventRefs.slice(0,index); 
                }else{ 
                  element[_EventRef]=_EventRefs.slice(0,index).concat(_EventRefs.slice(index+1,_EventRefs.length-1)); 
                } 
                break; 
              } 
            } 
            if(nestFn){ 
              element.detachEvent('on'+eventType,nestFn); 
            } 
          } 
        } 
      }; 
       
      //extend 
      (function(){ 
        //删除数组中指定下标出的元素 
        Array.prototype.remove = function(index){ 
          var o = this[index]; 
          if(typeof o !== "undefined"){ 
            if(index == 0){ 
              this.shift(); 
            }else if(index === this.length - 1){ 
              this.pop(); 
            }else{ 
              var arr1 = this.slice(0,index); 
              var arr2 = this.slice(index+1); 
              this.length = 0; 
              this.concat(arr1,arr2); 
            } 
          } 
        } 
        //删除数组中所有的元素 
        Array.prototype.clear = function(){ 
          this.length = 0; 
        } 
        //each 
        Array.prototype.each = function(fn){ 
          if(!util.isFunction(fn)){ 
            return; 
          } 
          for(var i=0;i<this.length;i++){ 
            if(typeof this[i] !== "undefined"){ 
              fn.call(this[i],i); 
            } 
          } 
        } 
         
        // 
        var collection = this.collection = function(){ 
          this.__data = {}; 
          this.length = 0; 
        } 
        collection.prototype = { 
          add:function(obj){ 
            this.__data[this.length++] = obj; 
          }, 
          get:function(index){ 
            return this.__data[index]; 
          }, 
          remove:function(index){ 
            var obj = this.__data[index]; 
            if(typeof obj !== "undefined"){ 
              this.length--; 
            } 
            delete this.__data[index]; 
          }, 
          clear:function(){ 
            this.__data = {}; 
            this.length = 0; 
          }, 
          each:function(fn){ 
            var index = 0; 
            for(var k in this.__data){ 
              if(k && typeof this.__data[k] !== "undefined"){ 
                fn.call(this.__data[k],index++); 
              } 
            } 
          }, 
          toArray:function(){ 
            var arr = []; 
            this.each(function(){ 
              arr.push(this); 
            }); 
            return arr; 
          } 
        }; 
      })(); 
      // 
      var grid = this.grid = function(table, options){ 
        grid.prototype._init.call(this,table,options); 
      } 
      grid.prototype = { 
        _init:function(table, options){ 
          if(typeof table === "undefined" || !table){ 
            throw "table is undefined or null"; 
          } 
          if(table.nodeType !== 1 || !/^table$/i.test(table.tagName)){ 
            throw "table must be 'table' element."; 
          } 
          table.guid = ++grid.guid; 
          this.__cache = {}; 
          var self = this; 
          var header = this.__cache["header"] = loadHeader();//header 
          this.__root = header.parentNode; 
          var templateRow = this.__cache["template"] = loadTemplate();//模板行 
          this.__cache["dataFormat"] = loadDataFormat();//数据模板 
          this.__cache["dataRows"] = new collection();//数据行 
          this.__cache["customCache"] = new collection();//用户缓存数据 
          this.__editRow = null;//当前编辑行 
          this.__saveContainer = createSaveButton();//保存按钮 
          this.__cache["commandHandles"] = {//command handels 
            removeRow:function(){ 
              var rowIndex = this.getAttribute("index"); 
              self.removeRow.apply(self,[rowIndex]); 
            }, 
            newLine:function(){ 
              self.newLine(); 
            } 
          }; 
          this.__regCommand = function(commandName,row){ //注册command 
            if(row){ 
              var arg = row.getAttribute("index"); 
              this.setAttribute("index",arg || false); 
            } 
            this.commandName = commandName; 
            Event.remove.call(this,"click",exec); 
            Event.on.call(this,"click",exec); 
          } 
          this.__removeRowCallback = function(){ //改变行的背景样式 
            var rows = this.__cache["dataRows"]; 
            var customCache = this.__cache["customCache"]; 
            var arr = rows.toArray(),dataArr=[]; 
            var rowIndex,row,data; 
             
            rows.clear(); 
            arr.each(function(i){ 
              rowIndex = this.getAttribute("index"); 
              data = customCache.get(rowIndex); 
              dataArr.push(data); 
              this.setAttribute("index",i.toString()); 
              rows.add(this); 
              if( i % 2 == 1){//基数行 
                if(!/\sodd\s|\sodd$/g.test(this.className)){ 
                  this.className = (this.className || "") + " odd"; 
                } 
              }else if(/\sodd\s|\sodd$/g.test(this.className)){ 
                this.className = this.className.replace(/\sodd\s|\sodd$/g," "); 
              } 
              i++; 
            }); 
             
            customCache.clear(); 
            dataArr.each(function(){ 
              customCache.add(this); 
            }); 
          }           
           
          //事件处理 
          options = options || {}; 
          this.onDataBinding = options.onDataBinding || this.onDataBinding; 
          this.onRowBinding = options.onRowBinding || this.onRowBinding; 
          this.onRowBinded = options.onRowBinded || this.onRowBinded;          
           
          function loadHeader(){ 
            var tr = table.firstChild; 
            if(tr && tr.nodeType != 1){ 
              tr = util.next(tr); 
            } 
            if(!/tr/i.test(tr.tagName)){ //如果第一个元素不是tr,则浏览器支持tbody 
              tr = tr.firstChild; 
              if(tr.nodeType != 1){ 
                tr = util.next(tr); 
              } 
            } 
            return tr; 
          } 
           
          function loadTemplate(){ 
            tr = util.next(header);//获取模板行 
            return tr; 
          } 
           
          function loadDataFormat(){ 
            var nodes = templateRow.childNodes,ele,data,result = {},attr; 
            nodes = util.parseArray(nodes); 
            nodes.each(function(i){ 
              ele = this; 
              if(ele && ele.nodeType == 1){ 
                attr = ele.data || ele.getAttribute("data"); 
                if(attr){ 
                  data = util.parseJSON(attr); 
                  ele.field = data.field; 
                  result[ele.field] = data; 
                } 
              } 
            });            
            return result; 
          } 
           
          function createSaveButton(){ 
            var div = document.createElement("div"); 
            div.style.position = "absolute"; 
            div.style.display = "none"; 
            div.style.width = "auto"; 
            var btn = document.createElement("button"); 
            btn.innerHTML = btn.innerText = btn.textContent = btn.value = "Save"; 
            try{ 
              btn.type = "button"; 
            }catch(e){ 
              btn.setAttribute("type","button"); 
            } 
            div.appendChild(btn); 
             
            var btnCancel = document.createElement("button"); 
            btnCancel.innerHTML = btnCancel.innerText = btnCancel.textContent = btnCancel.value = "Cancel"; 
            try{ 
              btnCancel.type = "button"; 
            }catch(e){ 
              btnCancel.setAttribute("type","button"); 
            } 
            div.appendChild(btnCancel); 
             
            document.body.appendChild(div); 
            Event.on.call(btn,"click",function(){ 
              self.save(); 
            }); 
            Event.on.call(btnCancel,"click",function(){ 
              div.style.display = "none"; 
              if(self.__editRow){ 
                self.__editRow.parentNode.removeChild(self.__editRow); 
                self.__editRow = null; 
              } 
            }); 
             
            return div; 
          } 
           
          function exec(){ 
            if(self.__editRow){//如果当前处于编辑模式,则禁用所有command 
              return; 
            } 
            var commandName = this.commandName; 
            var handler = self.__cache["commandHandles"][commandName];            
            if(handler){ 
              handler.call(this); 
            } 
          } 
           
          //去除模板行 
          templateRow.parentNode.removeChild(templateRow); 
           
          //处理表格中的command事件 
          var elements = header.getElementsByTagName("*"); 
          elements = util.parseArray(elements); 
          elements.each(function(){ 
            if(this.nodeType === 1){ 
              var commandName = this.command || this.getAttribute("command"); 
              if(commandName){ 
                self.__regCommand.call(this,commandName,header); 
              } 
            } 
          }); 
        }, 
        //bangding 
        bind:function(data){ 
          this.clear(); 
          if(data && data.length > 0){ 
            var self = this; 
            data.each(function(){ 
              self.append(this); 
            }); 
          } 
        }, 
        //清理表,删除所以除header以外的数据行 
        clear:function(){ 
          var rows = this.__cache["dataRows"],row; 
          rows.each(function(){ 
            row = this; 
            if(row){ 
              row.parentNode.removeChild(row); 
            } 
          }); 
          rows.clear();//清理rows 
        }, 
        //删除指定的行 
        removeRow:function(rowIndex){ 
          var rows = this.__cache["dataRows"]; 
          var row = rows.get(rowIndex); 
          if(row){ 
            var data = this.__cache["customCache"][rowIndex]; 
            row.parentNode.removeChild(row); 
            rows.remove(rowIndex); 
            //通知用户数据行被移除             
            if(util.isFunction(this.onRowRemoved)){ 
              this.onRowRemoved(data,row); 
            } 
          } 
          this.__removeRowCallback(); 
        }, 
        //添加 行  
        append:function(data){ 
          if(!data){ 
            return ; 
          } 
          var template = this.__cache["template"]; 
          var rows = this.__cache["dataRows"]; 
          var rowIndex = rows.length; 
          var tr = template.cloneNode(); 
          var customCache = this.__cache["customCache"]; 
          customCache.add(data); 
          //将数据行添加到table 
          this.__root.appendChild(tr); 
          var self = this; 
          var td,//数据单元格 
            dataFormat,//数据格式化器 
            value;//单元格中的给定的数据 
          tr.setAttribute("index",rowIndex.toString()); 
          //更改样式 
          if(rowIndex % 2 == 1){ 
            tr.className = (tr.className || "") + " odd"; 
          } 
          //通知 行数据绑定开始 
          if(util.isFunction(this.onRowBinding)){ 
            this.onRowBinding(rowIndex,tr); 
          } 
           
          var templateTD = template.firstChild; 
          while(templateTD){ 
            td = templateTD.cloneNode(true); 
            tr.appendChild(td); 
            if(td.nodeType == 1 && templateTD.field){ 
              dataFormat = this.__cache["dataFormat"][templateTD.field]; 
              td.removeAttribute("data"); 
              td.field = templateTD.field; 
              value = data[dataFormat.field]; 
              //通知单元格数据绑定事件 
              value = this.onDataBinding(dataFormat.field,value,td,data); 
              if(value !== false){//如果返回false,则不用做赋值操作 
                switch(dataFormat.render){ 
                  case "innerHTML": 
                    td.innerHTML = typeof value == "undefined" || value == null ? "" : value; 
                    break; 
                  case "innerText": 
                  default: 
                    td.innerText = td.textContent = typeof value == "undefined" || value == null ? "" : value; 
                    break; 
                } 
              } 
            } 
            templateTD = templateTD.nextSibling; 
          } 
          rows.add(tr); 
           
          //处理command 
          var elements = tr.getElementsByTagName("*"),ele,attr; 
          elements = util.parseArray(elements); 
          elements.each(function(){ 
            ele = this; 
            if(ele.nodeType == 1 && (ele.command || ele.getAttribute("command"))){ 
              attr = ele.command || ele.getAttribute("command"); 
              self.__regCommand.call(ele,attr,tr); 
            } 
          }); 
           
          //通知 行数据绑定完成 
          if(util.isFunction(this.onRowBinded)){ 
            this.onRowBinded(rowIndex,tr); 
          } 
        }, 
        //手动产生新的输入行 
        newLine:function(){ 
          if(this.__editRow){//如果当前有存在编辑行,则直接返回,每次最多限制编辑一行数据 
            return; 
          } 
          var template = this.__cache["template"] ; 
          var row = this.__editRow = template.cloneNode(false); 
          var templateTD = template.firstChild; 
          var textareaList = []; 
                       
          while(templateTD){ 
            td = templateTD.cloneNode(true); 
            row.appendChild(td); 
            if(td.nodeType == 1){ 
              if(templateTD.field){ 
                td.field = templateTD.field; 
                td.innerHTML = ""; 
                var dataFormat = this.__cache["dataFormat"][templateTD.field]; 
                var textarea = null; 
                if(dataFormat.render == "innerHTML"){ 
                  textarea = document.createElement("textarea"); 
                }else{ 
                  textarea = document.createElement("input"); 
                  textarea.type = "text"; 
                } 
                textarea.style.display = "none"; 
                td.appendChild(textarea); 
                textareaList.push(textarea); 
              } 
            } 
            templateTD = templateTD.nextSibling; 
          } 
          //将数据行添加到table 
          this.__root.appendChild(row); 
           
          var height = row.offsetHeight, 
            width = row.offsetWidth, 
            offset = util.offset(row); 
             
          textareaList.each(function(){ 
            this.style.height = (0.8 * height) + "px"; 
            this.style.width = (0.8 * this.parentNode.offsetWidth) + "px"; 
            this.style.display = ""; 
          }); 
           
          var left = offset.left + width + 5; 
          var top = offset.top; 
          this.__saveContainer.style.top = top + "px"; 
          this.__saveContainer.style.left = left + "px"; 
          this.__saveContainer.style.height = this.__saveContainer.style.lineHeight = height + "px"; 
          this.__saveContainer.style.display = "block"; 
        }, 
        //保存手动产生的数据行数据 
        save:function(){ 
          if(!this.__editRow){ 
            return; 
          } 
           
          var row = this.__editRow; 
          var td = row.firstChild; 
          var data = {}; 
          while(td){ 
            if(td.nodeType === 1 && td.field){ 
              var dataFormat = this.__cache["dataFormat"][td.field]; 
              var textarea = null; 
              if(dataFormat.render == "innerHTML"){ 
                textarea = td.getElementsByTagName("textarea")[0]; 
              }else{ 
                textarea = td.getElementsByTagName("input")[0]; 
              } 
              value = textarea.value; 
              switch(dataFormat.dataType){ 
                case "number": 
                  value = util.trim(value); 
                  value = Number(value.length == 0 ? 0 : value); 
                  break; 
                default: 
                  break; 
              } 
              data[td.field] = value; 
            } 
            td = td.nextSibling; 
          } 
          this.__editRow.parentNode.removeChild(this.__editRow); 
          this.__editRow = null; 
          this.__saveContainer.style.display = "none"; 
           
          //通知用户正在保存数据 
          if(util.isFunction(this.onSaving)){ 
            this.onSaving(data); 
          } 
           
          this.append(data); 
        }, 
        getRowData:function(rowIndex){ 
          return this.__cache["customCache"].get(rowIndex); 
        }, 
         
        //数据绑定到指定cell时的事件 
        onDataBinding:function(field,value,cell,data){ 
          return value; 
        }, 
        //当数据行绑定开始时的事件 
        onRowBinding:function(rowIndex, row){ 
        }, 
        //当数据行绑定完成时的事件 
        //@param row {DOM element tr}  
        onRowBinded:function(rowIndex, row){ 
        }, 
        //当编辑的数据被保存时的事件 
        onSaving:function(data){ 
        }, 
        //当数据行被移除时的通知事件 
        onRowRemoved:function(data,row){ 
        } 
      }; 
       
      grid.guid = 0; 
    })(); 
     
     
  </script> 
</head> 
 
<body> 
  <table id="table_demo" class="grid"> 
    <tr class="odd"> 
      <th>ID</th> 
      <th>Name</th> 
      <th>Descpription</th> 
      <th><button type="button" command="newLine" class="btn">New Line</button></th> 
    </tr> 
    <tr> 
      <td data='{"field":"id","dataType":"number","render":"innerText"}'>1</td> 
      <td data='{"field":"name","dataType":"string","render":"innerText"}'>WorkingService</td> 
      <td data='{"field":"description","dataType":"string","render":"innerHTML"}'>WorkingService</td> 
      <td> 
        <button type="button" command="removeRow" class="btn">Delete</button> 
      </td> 
    </tr> 
  </table> 
  <script type="text/javascript"> 
    var table = document.getElementById("table_demo"); 
    var g = new grid(table,{ 
      onDataBinding:function(field,value){ 
        return value; 
      }, 
      onRowBinded:function(rowIndex,row){} 
    }); 
    g.bind([ 
      {id:0,name:"kilin"}, 
      {id:1,name:"kilin1"}, 
      {id:2,name:"kilin2"}, 
      {id:3,name:"kilin3"} 
    ]); 
  </script> 
</body> 
</html>
Javascript 相关文章推荐
Javascript 中文字符串处理额外注意事项
Nov 15 Javascript
JSON 数据格式介绍
Jan 13 Javascript
Extjs 3.3切换tab隐藏相应工具栏出现空白解决
Apr 02 Javascript
给easyui的datebox控件添加清空按钮的实现方法
Nov 09 Javascript
JavaScript数据结构链表知识详解
Nov 21 Javascript
基于JS实现9种不同的面包屑和分布式多步骤导航效果
Feb 21 Javascript
JS实现两周内自动登录功能
Mar 23 Javascript
JS排序算法之希尔排序与快速排序实现方法
Dec 12 Javascript
React Router v4 入坑指南(小结)
Apr 08 Javascript
vue结合axios与后端进行ajax交互的方法
Jul 06 Javascript
JavaScript创建对象方法实例小结
Sep 03 Javascript
Bootstrap 时间日历插件bootstrap-datetimepicker配置与应用小结
May 28 Javascript
Javascript中call与apply的学习笔记
Sep 22 #Javascript
Javascript中this的用法详解
Sep 22 #Javascript
JavaScript的作用域和块级作用域概念理解
Sep 21 #Javascript
JavaScript中双叹号!!作用示例介绍
Sep 21 #Javascript
原生JavaScript实现合并多个数组示例
Sep 21 #Javascript
仿淘宝TAB切换搜索框搜索切换的相关内容
Sep 21 #Javascript
将数字转换成大写的人民币表达式的js函数
Sep 21 #Javascript
You might like
PHP中mb_convert_encoding与iconv函数的深入解析
2013/06/21 PHP
以文件形式缓存php变量的方法
2015/06/26 PHP
PHP Yaf框架的简单安装使用教程(推荐)
2016/06/08 PHP
Symfony2获取web目录绝对路径、相对路径、网址的方法
2016/11/14 PHP
javascript jQuery $.post $.ajax用法
2008/07/09 Javascript
Jquery Ajax学习实例3 向WebService发出请求,调用方法返回数据
2010/03/16 Javascript
JavaScript打印iframe内容示例代码
2013/08/20 Javascript
js打开新窗口方法整理
2014/02/17 Javascript
Angularjs过滤器使用详解
2016/05/25 Javascript
分享jQuery网页元素拖拽插件
2020/12/01 Javascript
解析JavaScript中的字符串类型与字符编码支持
2016/06/24 Javascript
jquery attr()设置和获取属性值实例教程
2016/09/25 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
2017/09/06 Javascript
Vue 项目分环境打包的方法示例
2018/08/03 Javascript
react 兄弟组件如何调用对方的方法示例
2018/10/23 Javascript
JavaScript实现新年倒计时效果
2018/11/17 Javascript
Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例
2020/11/18 Vue.js
Python文件的读写和异常代码示例
2017/10/31 Python
Python实现的随机森林算法与简单总结
2018/01/30 Python
python逐行读写txt文件的实例讲解
2018/04/03 Python
Python通过for循环理解迭代器和生成器实例详解
2019/02/16 Python
pytorch 模型的train模式与eval模式实例
2020/02/20 Python
Python实例教程之检索输出月份日历表
2020/12/16 Python
浅谈three.js中的needsUpdate的应用
2012/11/12 HTML / CSS
耐克巴西官方网站:Nike巴西
2016/08/14 全球购物
怎样声明子类
2013/07/02 面试题
银行出纳岗位职责
2013/11/25 职场文书
小学生美德少年事迹
2014/02/02 职场文书
测绘专业大学生职业生涯规划书
2014/02/10 职场文书
酒店爱岗敬业演讲稿
2014/09/02 职场文书
购房协议书范本(无房产证)
2014/10/07 职场文书
2014年学校党建工作总结
2014/11/11 职场文书
《棉鞋里的阳光》教学反思
2016/02/20 职场文书
Canvas三种动态画圆实现方法说明(小结)
2021/04/16 Javascript
jquery插件实现代码雨特效
2021/04/24 jQuery
Java实现超大Excel文件解析(XSSF,SXSSF,easyExcel)
2022/07/15 Java/Android