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 相关文章推荐
IE与Firefox下javascript getyear年份的兼容性写法
Dec 20 Javascript
javascript下arguments,caller,callee,call,apply示例及理解
Dec 24 Javascript
jquery实现带单选按钮的表格行选中时高亮显示
Aug 01 Javascript
JavaScript每天必学之基础知识
Sep 17 Javascript
jquery实现瀑布流效果 jquery下拉加载新数据
Dec 12 Javascript
浅谈javascript alert和confirm的美化
Dec 15 Javascript
Html5 js实现手风琴效果
Apr 17 Javascript
原生js实现移动端触摸轮播的示例代码
Dec 22 Javascript
微信小程序实现跑马灯效果完整代码(附效果图)
May 30 Javascript
一起写一个即插即用的Vue Loading插件实现
Oct 31 Javascript
vue实现列表滚动的过渡动画
Jun 29 Javascript
Vue-router编程式导航的两种实现代码
Mar 04 Vue.js
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 中检查或过滤IP地址的实现代码
2011/11/27 PHP
PHP笔记之:基于面向对象设计的详解
2013/05/14 PHP
PHP将HTML转换成文本的实现代码
2015/01/21 PHP
php封装的page分页类完整实例
2016/10/18 PHP
File, FileReader 和 Ajax 文件上传实例分析(php)
2011/04/27 Javascript
Javascript异步编程的4种方法让你写出更出色的程序
2013/01/17 Javascript
文本框只能选择数据到文本框禁止手动输入
2013/11/22 Javascript
js图片自动轮播代码分享(js图片轮播)
2014/05/06 Javascript
java、javascript实现附件下载示例
2014/08/14 Javascript
JS数组的常见用法实例
2015/02/10 Javascript
JavaScript几种数组去掉重复值的方法推荐
2016/04/12 Javascript
Javascript操作表单实例讲解(下)
2016/06/20 Javascript
JavaScript基础知识点归纳(推荐)
2016/07/09 Javascript
javascript实现的图片预览功能
2017/03/25 Javascript
快速使用node.js进行web开发详解
2017/04/26 Javascript
jQuery实现的简单动态添加、删除表格功能示例
2017/09/21 jQuery
基于JavaScript实现报警器提示音效果
2017/10/27 Javascript
vue采用EventBus实现跨组件通信及注意事项小结
2018/06/14 Javascript
[01:10:58]KG vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
Python对两个有序列表进行合并和排序的例子
2014/06/13 Python
python学习教程之使用py2exe打包
2017/09/24 Python
简单了解什么是神经网络
2017/12/23 Python
Python PyQt4实现QQ抽屉效果
2018/04/20 Python
python爬虫框架scrapy实现模拟登录操作示例
2018/08/02 Python
Python一句代码实现找出所有水仙花数的方法
2018/11/13 Python
pandas 使用均值填充缺失值列的小技巧分享
2019/07/04 Python
python tqdm 实现滚动条不上下滚动代码(保持一行内滚动)
2020/02/19 Python
Python如何执行精确的浮点数运算
2020/07/31 Python
python speech模块的使用方法
2020/09/09 Python
CSS3制作翻转效果_动力节点Java学院整理
2017/07/11 HTML / CSS
美国在线面料商店:Fashion Fabrics Club
2020/01/31 全球购物
什么是"引用"?申明和使用"引用"要注意哪些问题?
2016/03/03 面试题
宝宝满月酒主持词和仪式流程
2014/03/27 职场文书
质量月活动总结
2014/08/26 职场文书
教师批评与自我批评心得体会
2014/10/16 职场文书
php实现自动生成验证码的实例讲解
2021/11/17 PHP