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 相关文章推荐
jquery命令汇总,方便使用jquery的朋友
Jun 26 Javascript
html5 canvas js(数字时钟)实例代码
Dec 23 Javascript
node.js中的forEach()是同步还是异步呢
Jan 29 Javascript
jquery实现简单的轮换出现效果实例
Jul 23 Javascript
JavaScript Promise 用法
Jun 14 Javascript
Bootstrap Img 图片样式(推荐)
Dec 13 Javascript
JS组件系列之MVVM组件 vue 30分钟搞定前端增删改查
Apr 28 Javascript
原生JS实现N级菜单的代码
May 21 Javascript
jQuery常见面试题之DOM操作详析
Jul 05 jQuery
vue实现element表格里表头信息提示功能(推荐)
Nov 20 Javascript
用VsCode编辑TypeScript的实现方法
May 07 Javascript
javascript的var与let,const之间的区别详解
Feb 18 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
综合图片计数器
2006/10/09 PHP
基于php缓存的详解
2013/05/15 PHP
Windows下的PHP安装文件线程安全和非线程安全的区别
2014/04/23 PHP
主流PHP框架的优缺点对比分析
2014/12/25 PHP
yii实现model添加默认值的方法(2种方法)
2016/01/06 PHP
浅谈mysql_query()函数的返回值问题
2016/09/05 PHP
PHP入门教程之PHP操作MySQL的方法分析
2016/09/11 PHP
PHP中递归的实现实例详解
2017/11/14 PHP
PHP排序算法之堆排序(Heap Sort)实例详解
2018/04/21 PHP
Prototype使用指南之form.js
2007/01/10 Javascript
读jQuery之五(取DOM元素)
2011/06/20 Javascript
文本框input聚焦失焦样式实现代码
2012/10/12 Javascript
jquery获得keycode的示例代码
2013/12/30 Javascript
JS完整获取IE浏览器信息包括类型、版本、语言等等
2014/05/22 Javascript
整理Javascript函数学习笔记
2015/12/01 Javascript
vue如何实现observer和watcher源码解析
2017/03/09 Javascript
VUE 更好的 ajax 上传处理 axios.js实现代码
2017/05/10 Javascript
vue+vuex+axios+echarts画一个动态更新的中国地图的方法
2017/12/19 Javascript
js生成word中图片处理方法
2018/01/06 Javascript
Node.js文件编码格式的转换的方法
2018/04/27 Javascript
bootstrap动态调用select下拉框的实例代码
2018/08/09 Javascript
微信小程序自定义多列选择器使用详解
2019/06/21 Javascript
python实现提取百度搜索结果的方法
2015/05/19 Python
基于Django的ModelForm组件(详解)
2017/12/07 Python
对python中数据集划分函数StratifiedShuffleSplit的使用详解
2018/12/11 Python
使用Python自动化破解自定义字体混淆信息的方法实例
2019/02/13 Python
解决django 新增加用户信息出现错误的问题
2019/07/28 Python
Python自动化测试笔试面试题精选
2020/03/12 Python
Python非单向递归函数如何返回全部结果
2020/12/18 Python
美国在线家居装饰店:Belle&June
2018/10/24 全球购物
留学推荐信中文范文三篇
2014/01/25 职场文书
开办大学饮食联盟创业计划书
2014/01/29 职场文书
建筑专业毕业生自荐信
2014/05/25 职场文书
食堂厨师岗位职责
2014/08/25 职场文书
病人慰问信范文
2015/02/15 职场文书
大学军训通讯稿
2015/07/18 职场文书