jQuery EasyUI框架中的Datagrid数据表格组件结构详解


Posted in Javascript onJune 09, 2016

基础DOM结构
什么叫“完整的基础DOM结构”,这里“基础”的意思是指这个结构不依赖具体数据,不依赖Datagrid的view属性,只要存在Datagrid实例就会存在这样的基础DOM结构;而“完整”的意思是指在冻结列,冻结行,标题,footer,分页这些功能块都存在时候的DOM结构。

要搞清楚Datagrid的工作原理,这个DOM结构必须要烂熟于胸的,我们直接来看这个“基础完整DOM结构”是什么样子的:

<!-- datagrid的最外层容器,可以使用$(target).datagrid('getPanel')或者$.data(target,'datagrid').panel得到这个DOM对象,这个DOM上其实承载了panel组件-->   
<div class="panel datagrid">   
  <!-- datagrid的标题区域容器,对应于panel组件的header部分,可以使用$(target).datagrid('getPanel').panel('header')得到这个DOM对象-->   
  <div class="panel-header">   
    <div class="panel-title"></div>   
    <div class="panel-tool"></div>   
  </div>   
  <!-- datagrid的主体区域容器,对应于panel组件的body部分,可以使用$(target).datagrid('getPanel').panel('body')得到这个DOM对象-->   
  <div class="datagrid-wrap panel-body"> 
    <!--工具栏--> 
    <div class="datagrid-toolbar"></div>   
    <!-- datagrid视图部分的容器,这是datagrid组件DOM结构的核心,其基础视图结构跟datagrid的view属性无任何关系。-->   
    <!-- 对应dc.view -->   
    <div class="datagrid-view">   
      <!-- div.datagrid-view1负责展示冻结列部分(包含行号或者frozenColumns)的数据-->   
      <!-- 对应dc.view1 -->   
      <div class="datagrid-view1">   
        <!--列标题部分-->   
        <div class="datagrid-header">   
          <!-- 对应dc.header1 -->   
          <div class="datagrid-header-inner">   
            <!--样式里有htable关键字,h代表header的意思-->   
            <table class="datagrid-htable">   
              <tbody>   
                <tr class="datagrid-header-row"></tr>   
              </tbody>   
            </table>   
          </div>   
        </div>   
        <!--列数据部分-->   
        <div class="datagrid-body">   
          <!-- 对应dc.body1 -->   
          <div class="datagrid-body-inner">   
            <!--frozenRows部分(有数据才会有这个table,故不属于基础DOM结构),固定行是1.3.2版本之后才加的功能,注意datagrid-btable-frozen关键样式,btable代码body table的意思-->   
            <table class="datagrid-btable datagrid-btable-frozen"></table>   
            <!--普通rows部分(有数据才会有这个table,故不属于基础DOM结构)-->   
            <table class="datagird-btable"></table>   
          </div>   
        </div>   
        <!--footer部分-->   
        <div class="datagrid-footer">   
          <!-- 对应dc.footer1 -->   
          <div class="datagrid-footer-inner">   
            <!--ftable代表footer table的意思-->   
            <table class="datagrid-ftable"></table>   
          </div>   
        </div>   
      </div>   
      <!-- div.datagrid-view2负责展示非冻结列部分的数据,大家注意到冻结列和普通列视图是分开的,也就是说冻结列和普通列是在不同表格中展示的,这样会产生一个问题,那就是两个表格行高之间的同步问题。-->   
      <!-- 对应dc.view2 -->   
      <div class="datagrid-view2">   
        <!--列标题部分-->   
        <div class="datagrid-header">   
          <!-- 对应dc.header2 -->   
          <div class="datagrid-header-inner">   
            <table class="datagrid-htable">   
              <tbody>   
                <tr class="datagrid-header-row"></tr>   
              </tbody>   
            </table>   
          </div>   
        </div>   
        <!--列数据部分,注意这里并无datagrid-body-inner这个子元素,而冻结列对应的body却是有的,这个是细微区别-->   
        <!-- 对应dc.body2 -->   
        <div class="datagrid-body">   
          <!--frozenRows部分有数据才会有这个table,故不属于基础DOM结构,固定行是1.3.2版本之后才加的功能,-->   
          <table class="datagrid-btable datagrid-btable-frozen"></table>   
          <table class="datagrid-btable"></table>   
        </div>   
        <!--footer部分-->   
        <div class="datagrid-footer">   
          <!-- 对应dc.footer2 -->   
          <div class="datagrid-footer-inner">   
            <table class="datagrid-ftable"></table>   
          </div>   
        </div>   
      </div>   
    </div>   
    <!--分页部分-->   
    <div class="datagrid-pager pagination"></div>   
  </div>   
</div>

对于这个DOM结构,我在html代码里面已经做了简单说明,这里提一下绑定于Datagrid宿主table上的对象的dc属性,这个dc属性存储了对DOM结构里不同部分的引用,获取dc属性的方法:

$.data(target,'datagrid').dc;

而dc属性跟DOM的对应关系,我也在html中做了详细注释,请大家自行查看,这些都是我们深入认识Datagrid组件的基础。

默认视图分析
上面对Datagrid组件的骨架做了很详细的描述。有了骨架还并不完整,还得有血有肉有衣服穿才行。强大的Datagrid组件允许我们自己定义如何在基础骨架上长出健壮诱人的身体,我们只要定义Datagrid的视图就可以实现。

在大多数情况下,我们并无特别要求,Datagrid给我们提供了默认的视图,默认视图被使用在90%以上的场景,所以对默认视图的分析显得非常有必要。注意视图里面定义了哪些接口,哪些方法,如果要自己写视图的话,最好把这些接口和方法都写齐全。

var view = {  
  /** 
   * 填充表格主体数据(生成数据部分的各行tr) 
   * @param {DOM object} target  datagrid宿主table对应的DOM对象 
   * @param {DOM object} container 数据主体容器。包含两个可能的值,即: 
   * 1.frozen部分body1,对应的DOM对象为:div.datagrid-view>div.datagrid-view1>div.datagrid-body>div.datagrid-body-inner 
   * 2.常规部分body2,对应的DOM对象为:div.datagrid-view>div.datagrid-view2>div.datagrid-body 
   * @param {boolean} frozen  是否是冻结列 
   * @return {undefined}      未返回值 
   */ 
  render: function(target, container, frozen) {  
    var data = $.data(target, "datagrid");  
    var opts = data.options;  
    var rows = data.data.rows;  
    var fields = $(target).datagrid("getColumnFields", frozen);  
    if(frozen) {  
      //如果grid不显示rownumbers并且也没有frozenColumns的话,直接退出。  
      if(!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {  
        return;  
      }  
    }  
    //定义表格字符串,注意这里使用了数组的join方式代替了传统的"+"运算符,在大多浏览器中,这样效率会更高些。  
    var html = ["<table class=\"datagrid-btable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>"];  
    for(var i = 0; i < rows.length; i++) {  
      //striped属性,用于设置grid数据是否隔行变色,当然了实现原理很简单。  
      var cls = (i % 2 && opts.striped) ? "class=\"datagrid-row datagrid-row-alt\"" : "class=\"datagrid-row\"";  
      /** 
       * 表格的rowStyler属性用于处理数据行的css样式,当然了这个样式仅仅是作用于tr标签上。 
       * 这地方使用call了方法来设置上下文,如果rowStyler函数内部使用了this的话,则this指向datagrid的宿主table对应的DOM对象。 
       */ 
      var style = opts.rowStyler ? opts.rowStyler.call(target, i, rows[i]) : "";  
      var styler = style ? "style=\"" + style + "\"" : "";  
      /** 
       * rowId:行的唯一标示,对应于tr的id属性,其由以下几部分组成: 
       * 1.字符窜常量:"datagrid-row-r"; 
       * 2.全局索引index:该索引值从1开始递增,同一个datagrid组件实例拥有唯一值,如果同一页面内有多个datagrid实例,那么其值从1递增分配给每个datagrid实例; 
       * 3.冻结列标识frozen:该标识用于标示是否是冻结列(包含行号和用户指定的frozenColumns),"1"代表冻结列,"2"代表非冻结列; 
       * 4.行数索引:该值才是真正代表“第几行”的意思,该值从0开始递增 
       * 如页面内第一个datagrid实例的非冻结列第10行数据的rowId为"datagrid-row-r1-2-9" 
       */ 
      var rowId = data.rowIdPrefix + "-" + (frozen ? 1 : 2) + "-" + i;  
      html.push("<tr id=\"" + rowId + "\" datagrid-row-index=\"" + i + "\" " + cls + " " + styler + ">");  
      /** 
       * 调用renderRow方法,生成行数据(行内的各列数据)。 
       * 这里的this就是opts.view,之所以用call方法,只是为了传参进去。这里我们使用this.renderRow(target,fields,frozen,i,rows[i])来调用renderRow方法应该也是可以的。 
       */ 
      html.push(this.renderRow.call(this, target, fields, frozen, i, rows[i]));  
      html.push("</tr>");  
    }  
    html.push("</tbody></table>");  
    //用join方法完成字符创拼接后直接innerHTML到容器内。  
    $(container).html(html.join(""));  
  },  
  /** 
   * [renderFooter  description] 
   * @param {DOM object} target  datagrid宿主table对应的DOM对象 
   * @param {DOM object} container 可能为dc.footer1或者dc.footer2 
   * @param {boolean} frozen  是否为frozen区 
   * @return {undefined}      未返回值 
   */ 
  renderFooter: function(target, container, frozen) {  
    var opts = $.data(target, "datagrid").options;  
    //获取footer数据  
    var rows = $.data(target, "datagrid").footer || [];  
    var columnsFields = $(target).datagrid("getColumnFields", frozen);  
    //生成footer区的table  
    var footerTable = ["<table class=\"datagrid-ftable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>"];  
    for(var i = 0; i < rows.length; i++) {  
      footerTable.push("<tr class=\"datagrid-row\" datagrid-row-index=\"" + i + "\">");  
      footerTable.push(this.renderRow.call(this, target, columnsFields, frozen, i, rows[i]));  
      footerTable.push("</tr>");  
    }  
    footerTable.push("</tbody></table>");  
    $(container).html(footerTable.join(""));  
  },  
  /** 
   * 生成某一行数据 
   * @param {DOM object} target  datagrid宿主table对应的DOM对象 
   * @param {array} fields  datagrid的字段列表 
   * @param {boolean} frozen  是否为冻结列 
   * @param {number} rowIndex 行索引(从0开始) 
   * @param {json object} rowData 某一行的数据 
   * @return {string}     单元格的拼接字符串 
   */ 
  renderRow: function(target, fields, frozen, rowIndex, rowData) {  
    var opts = $.data(target, "datagrid").options;  
    //用于拼接字符串的数组  
    var cc = [];  
    if(frozen && opts.rownumbers) {  
      //rowIndex从0开始,而行号显示的时候是从1开始,所以这里要加1.  
      var rowNumber = rowIndex + 1;  
      //如果分页的话,根据页码和每页记录数重新设置行号  
      if(opts.pagination) {  
        rowNumber += (opts.pageNumber - 1) * opts.pageSize;  
      }  
      /** 
       * 先拼接行号列 
       * 注意DOM特征,用zenCoding可表达为"td.datagrid-td-rownumber>div.datagrid-cell-rownumber" 
       */ 
      cc.push("<td class=\"datagrid-td-rownumber\"><div class=\"datagrid-cell-rownumber\">" + rowNumber + "</div></td>");  
    }  
    for(var i = 0; i < fields.length; i++) {  
      var field = fields[i];  
      var col = $(target).datagrid("getColumnOption", field);  
      if(col) {  
        var value = rowData[field];  
        //获取用户定义的单元格样式,入参包括:单元格值,当前行数据,当前行索引(从0开始)  
        var style = col.styler ? (col.styler(value, rowData, rowIndex) || "") : "";  
        //如果是隐藏列直接设置display为none,否则设置为用户想要的样式  
        var styler = col.hidden ? "style=\"display:none;" + style + "\"" : (style ? "style=\"" + style + "\"" : "");  
        cc.push("<td field=\"" + field + "\" " + styler + ">");  
        //如果当前列是datagrid组件保留的ck列时,则忽略掉用户定义的样式,即styler属性对datagrid自带的ck列是不起作用的。  
        if(col.checkbox) {  
          var styler = "";  
        } else {  
          var styler = "";  
          //设置文字对齐属性  
          if(col.align) {  
            styler += "text-align:" + col.align + ";";  
          }  
          //设置文字超出td宽时是否自动换行(设置为自动换行的话会撑高单元格)  
          if(!opts.nowrap) {  
            styler += "white-space:normal;height:auto;";  
          } else {  
            /** 
             * 并不是nowrap属性为true单元格就肯定不会被撑高,这还得看autoRowHeight属性的脸色 
             * 当autoRowHeight属性为true的时候单元格的高度是根据单元格内容而定的,这种情况主要是用于表格里展示图片等媒体。 
             */ 
            if(opts.autoRowHeight) {  
              styler += "height:auto;";  
            }  
          }  
        }  
        //这个地方要特别注意,前面所拼接的styler属性并不是作用于td标签上,而是作用于td下的div标签上。  
        cc.push("<div style=\"" + styler + "\" ");  
        //如果是ck列,增加"datagrid-cell-check"样式类  
        if(col.checkbox) {  
          cc.push("class=\"datagrid-cell-check ");  
        }  
        //如果是普通列,增加"datagrid-cell-check"样式类  
        else {  
          cc.push("class=\"datagrid-cell " + col.cellClass);  
        }  
        cc.push("\">");  
        /** 
         * ck列光设置class是不够的,当突然还得append一个input进去才是真正的checkbox。此处未设置input的id,只设置了name属性。 
         * 我们注意到formatter属性对datagird自带的ck列同样不起作用。 
         */ 
        if(col.checkbox) {  
          cc.push("<input type=\"checkbox\" name=\"" + field + "\" value=\"" + (value != undefined ? value : "") + "\"/>");  
        }  
        //普通列  
        else {  
          /** 
           * 如果单元格有formatter,则将formatter后生成的DOM放到td>div里面 
           * 换句话说,td>div就是如来佛祖的五指山,而formatter只是孙猴子而已,猴子再怎么变化翻跟头,始终在佛祖手里。 
           */ 
          if(col.formatter) {  
            cc.push(col.formatter(value, rowData, rowIndex));  
          }  
          //操,这是最简单的简况了,将值直接放到td>div里面。  
          else {  
            cc.push(value);  
          }  
        }  
        cc.push("</div>");  
        cc.push("</td>");  
      }  
    }  
    //返回单元格字符串,注意这个函数内部并未把字符串放到文档流中。  
    return cc.join("");  
  },  
  /** 
   * 刷新行数据,只有一个行索引(从0开始),调用的updateRow方法,这里直接跳过。 
   * @param {DOM object} target  datagrid实例的宿主table对应的DOM对象 
   * @param {number} rowIndex 行索引(从0开始) 
   * @return {undefined}     未返回数据 
   */ 
  refreshRow: function(target, rowIndex) {  
    this.updateRow.call(this, target, rowIndex, {});  
  },  
  /** 
   * 刷新行数据,该接口方法肩负着同步行高,重新计算和布局grid面板等重任 
   * @param {DOM object} target  datagrid实例的宿主table对应的DOM对象 
   * @param {number} rowIndex 行索引(从0开始) 
   * @param {json object} 行数据 
   * @return {undefined}     未返回数据 
   */ 
  updateRow: function(target, rowIndex, row) {  
    var opts = $.data(target, "datagrid").options;  
    var rows = $(target).datagrid("getRows");  
    $.extend(rows[rowIndex], row);  
    var style = opts.rowStyler ? opts.rowStyler.call(target, rowIndex, rows[rowIndex]) : "";  
 
    function updateTableRow(frozen) {  
      var fields = $(target).datagrid("getColumnFields", frozen);  
      //这个地方查找grid的数据主体表格(可能包含冻结列对应的主体表格和普通列对应的主体表格)  
      //getTr这个函数,我在博客上介绍过,请参考:http://www.easyui.info/archives/396.html  
      var tr = opts.finder.getTr(target, rowIndex, "body", (frozen ? 1 : 2));  
      var checked = tr.find("div.datagrid-cell-check input[type=checkbox]").is(":checked");  
      //这里调用了renderRow方法来重新获取当前行的html字符串  
      tr.html(this.renderRow.call(this, target, fields, frozen, rowIndex, rows[rowIndex]));  
      tr.attr("style", style || "");  
      //更新的时候保留checkbox状态(包含两层信息:一是有ck列;二是ck列被之前就被选中)  
      if(checked) {  
        tr.find("div.datagrid-cell-check input[type=checkbox]")._propAttr("checked", true);  
      }  
    };  
    //更新冻结列对应的行  
    updateTableRow.call(this, true);  
    //更新普通列对应的行  
    updateTableRow.call(this, false);  
    //重新布局表格面板  
    $(target).datagrid("fixRowHeight", rowIndex);  
  },  
  insertRow: function(target, rowIndex, row) {  
    var state = $.data(target, "datagrid");  
    //options  
    var opts = state.options;  
    //document of datagrid  
    var dc = state.dc;  
    var data = state.data;  
    //兼容无效的rowIndex,默认设置为在最后一行追加  
    if(rowIndex == undefined || rowIndex == null) {  
      rowIndex = data.rows.length;  
    }  
    //为啥不跟上面的条件并到一起,真是蛋疼  
    if(rowIndex > data.rows.length) {  
      rowIndex = data.rows.length;  
    }  
    /** 
     * 下移rows 
     * @param {boolean} frozen 是否为frozen部分 
     * @return {undefined}    无返回值 
     */ 
    function moveDownRows(frozen) {  
      //1:冻结列部分;2:普通列部分  
      var whichBody = frozen ? 1 : 2;  
      for(var i = data.rows.length - 1; i >= rowIndex; i--) {  
        var tr = opts.finder.getTr(target, i, "body", whichBody);  
        //注意这地方设置了tr的"datagrid-row-index"和"id"属性  
        tr.attr("datagrid-row-index", i + 1);  
        tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i + 1));  
        //计算行号  
        if(frozen && opts.rownumbers) {  
          //因rowIndex从0开始,以及须插入位置以下的tr要统一下移,所以新行号为i+2  
          var rownumber = i + 2;  
          //有分页的话,行号还要加上分页数据  
          if(opts.pagination) {  
            rownumber += (opts.pageNumber - 1) * opts.pageSize;  
          }  
          tr.find("div.datagrid-cell-rownumber").html(rownumber);  
        }  
      }  
    };  
    /** 
     * 插入了,要插两个地方的哦(如果你是男人,你可以淫荡地笑一下) 
     * @param {boolean} frozen 是否是frozen部分 
     * @return {undefined}    未返回值 
     */ 
    function doInsert(frozen) {  
      var whichBody = frozen ? 1 : 2;  
      //这行代码,不知道是干嘛的,怕插入得太快而早早缴械,所以才故意拖延时间的么?  
      var columnFields = $(target).datagrid("getColumnFields", frozen);  
      //构造新插入行的id属性  
      var trId = state.rowIdPrefix + "-" + whichBody + "-" + rowIndex;  
      var tr = "<tr id=\"" + trId + "\" class=\"datagrid-row\" datagrid-row-index=\"" + rowIndex + "\"></tr>";  
      if(rowIndex >= data.rows.length) {  
        //如果已经有记录,则插入tr即可  
        if(data.rows.length) {  
          //嗯哼,getTr的这个用法不多哦,未传入行索引,第三个参数为"last",随便的意淫一下就知道是获取最后一行了  
          //然后再在最后一行后插入一行,注意了,这里用的后入式  
          opts.finder.getTr(target, "", "last", whichBody).after(tr);  
        }  
        //如果表格尚无记录,则要生成表格,同时插入tr  
        else {  
          var cc = frozen ? dc.body1 : dc.body2;  
          cc.html("<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>" + tr + "</tbody></table>");  
        }  
      }  
      //在rowIndex + 1前准确无误地插入,注意了,这里是前入式。  
      else {  
        opts.finder.getTr(target, rowIndex + 1, "body", whichBody).before(tr);  
      }  
    };  
    //下移frozen部分  
    moveDownRows.call(this, true);  
    //下移普通列部分  
    moveDownRows.call(this, false);  
    //插入frozen区  
    doInsert.call(this, true);  
    //插入普通区  
    doInsert.call(this, false);  
    //总数加1  
    data.total += 1;  
    //维护data.rows数组,这地方是插入一个数组元素了  
    data.rows.splice(rowIndex, 0, row);  
    //刷新,其中包含了重新布局grid面板等复杂得一笔的操作  
    //插入本是件很简单愉快的事情,可是你得为其后果负上沉重的代价  
    this.refreshRow.call(this, target, rowIndex);  
  },  
  /** 
   * 删除行接口 
   * @param {DOM object} target datagrid实例的宿主table对应的DOM对象 
   * @param {number} rowIndex 行索引 
   * @return {undefined}    未返回值 
   */ 
  deleteRow: function(target, rowIndex) {  
    var state = $.data(target, "datagrid");  
    var opts = state.options;  
    var data = state.data;  
 
    function moveUpRows(frozen) {  
      var whichBody = frozen ? 1 : 2;  
      for(var i = rowIndex + 1; i < data.rows.length; i++) {  
        var tr = opts.finder.getTr(target, i, "body", whichBody);  
        //"datagrid-row-index"和"id"属性减一  
        tr.attr("datagrid-row-index", i - 1);  
        tr.attr("id", state.rowIdPrefix + "-" + whichBody + "-" + (i - 1));  
        if(frozen && opts.rownumbers) {  
          var rownumber = i;  
          if(opts.pagination) {  
            rownumber += (opts.pageNumber - 1) * opts.pageSize;  
          }  
          tr.find("div.datagrid-cell-rownumber").html(rownumber);  
        }  
      }  
    };  
    //移除行  
    opts.finder.getTr(target, rowIndex).remove();  
    //上移frozen区  
    moveUpRows.call(this, true);  
    //上移普通区  
    moveUpRows.call(this, false);  
    //记录数减一  
    data.total -= 1;  
    //维护data.rows数据  
    data.rows.splice(rowIndex, 1);  
  },  
  /** 
   * 默认的onBeforeRender事件 为空 
   * @param {DOM object} target datagrid实例的宿主table对应的DOM对象 
   * @param {array} rows 要插入的数据 
   * @return {undefined}    默认未返回值 
   */ 
  onBeforeRender: function(target, rows) {},  
  /** 
   * 默认的onAfterRender 隐藏footer里的行号和check 
   * @param {DOM object} target datagrid实例的宿主table对应的DOM对象 
   * @return {undefined}    未返回值 
   */ 
  onAfterRender: function(target) {  
    var opts = $.data(target, "datagrid").options;  
    if(opts.showFooter) {  
      var footer = $(target).datagrid("getPanel").find("div.datagrid-footer");  
      footer.find("div.datagrid-cell-rownumber,div.datagrid-cell-check").css("visibility", "hidden");  
    }  
  }  
};
Javascript 相关文章推荐
jQuery基本选择器选择元素使用介绍
Apr 18 Javascript
JS 操作Array数组的方法及属性实例解析
Jan 08 Javascript
对table和ul实现js分页示例分享
Feb 24 Javascript
JS实现的文字与图片定时切换效果代码
Oct 06 Javascript
异步加载JS、CSS代码(推荐)
Jun 15 Javascript
微信小程序开发入门基础教程
Apr 19 Javascript
AngularJS自定义指令之复制指令实现方法
May 18 Javascript
jQuery选择器之属性过滤选择器详解
Sep 28 jQuery
bootstrap table sum总数量统计实现方法
Oct 29 Javascript
Next.js项目实战踩坑指南(笔记)
Nov 29 Javascript
一个因@click.stop引发的bug的解决
Jan 08 Javascript
JavaScript设计模型Iterator实例解析
Jan 22 Javascript
Active控件问题小结(附解决办法)
Jun 09 #Javascript
JS 通过系统时间限定动态添加 select option的实例代码
Jun 09 #Javascript
JavaScript的instanceof运算符学习教程
Jun 08 #Javascript
JavaScript中instanceof运算符的使用示例
Jun 08 #Javascript
实例讲解JavaScript中instanceof运算符的用法
Jun 08 #Javascript
js获取对象、数组的实际长度,元素实际个数的实现代码
Jun 08 #Javascript
JS &amp; JQuery 动态添加 select option
Jun 08 #Javascript
You might like
php面向对象全攻略 (十) final static const关键字的使用
2009/09/30 PHP
获取PHP警告错误信息的解决方法
2013/06/03 PHP
PHP采用XML-RPC构造Web Service实例教程
2014/07/16 PHP
php打印输出棋盘的实现方法
2014/12/23 PHP
PHP中Restful api 错误提示返回值实现思路
2016/04/12 PHP
javascript获取当前ip的代码
2009/05/10 Javascript
JS获取鼠标坐标的实例方法
2013/07/18 Javascript
基于jQuery实现选取月份插件附源码下载
2015/12/28 Javascript
理解JavaScript表单的基础知识
2016/01/25 Javascript
Bootstrap编写一个同时适用于PC、平板、手机的登陆页面
2016/06/30 Javascript
jquery实现垂直和水平菜单导航栏
2020/08/27 Javascript
jQuery条件分页 代替离线查询(附代码)
2017/08/17 jQuery
理解javascript async的用法
2017/08/22 Javascript
使用JS动态显示文本
2017/09/09 Javascript
浅谈如何使用 webpack 优化资源
2017/10/20 Javascript
Angular实现较为复杂的表格过滤,删除功能示例
2017/12/23 Javascript
详解基于React.js和Node.js的SSR实现方案
2019/03/21 Javascript
JS解惑之Object中的key是有序的么
2019/05/06 Javascript
koa2的中间件功能及应用示例
2020/03/05 Javascript
vue使用echarts图表自适应的几种解决方案
2020/12/04 Vue.js
[43:24]完美世界DOTA2联赛PWL S3 INK ICE vs DLG 第二场 12.12
2020/12/17 DOTA
Python语言实现机器学习的K-近邻算法
2015/06/11 Python
Python元组 tuple的概念与基本操作详解【定义、创建、访问、计数、推导式等】
2019/10/30 Python
python中自带的三个装饰器的实现
2019/11/08 Python
keras 多gpu并行运行案例
2020/06/10 Python
Python collections.defaultdict模块用法详解
2020/06/18 Python
基于django2.2连oracle11g解决版本冲突的问题
2020/07/02 Python
python入门:argparse浅析 nargs='+'作用
2020/07/12 Python
有关HTML5 Video对象的ontimeupdate事件(Chrome上无效)的问题
2013/07/19 HTML / CSS
怎样让char类型的东西转换成int类型
2013/12/09 面试题
计算机网络毕业生自荐信
2013/10/01 职场文书
大学自荐信
2013/12/12 职场文书
彩色的翅膀教学反思
2014/04/25 职场文书
学校文明单位申报材料
2014/05/06 职场文书
商务英语专业求职信
2014/06/26 职场文书
zabbix 代理服务器的部署与 zabbix-snmp 监控问题
2022/07/15 Servers