列表插件的详解

此列表插件,是用来显示后台的大数据的。比如:后台有几万条数据,需要一列一列的显示出来。但是由于是插件,所以应该能兼容各种数据的展示。有些数据的选项多,有些数据后面需要操作的按钮(操作的按钮也有可能是多个)。废话不说,直接上代码。

function List (options) {
  this.dataResoure = options.dataResoure;      //数据源,要显示的数据
  this.key = options.key;                //主键;
  this.operateList = options.operateList;        //操作按钮的对象数组,数组里面有多少个,就有多少个操作按钮;

  //对象的格式{text:"",callback:callback,bingColums:[]};text是操作按钮的显示的文字,callback是点击按钮时,触发的方法。bingColums是callback需要操作多个属性值时,传入的属性名的数组
  this.isCheckButton = options.isCheckButton;  //在每一行前面是否有多选(或单选)按钮;true的话,就表示默认显示。
  this.bingContorlId = options.bingContorlId;  //需要将生成的HTML添加到容器元素的ID;
  this.columns = options.columns;  //列表的属性对象的数组,数组中每一项就是下面的这个对象,每个对象就代表每一行中的一个选项,数组有多少项,列表的每一行就显示多少个选项,下面的这个对象,就是一行中的一个选项。

  /*
  {
    "columnId":"",  //显示的列的ID
    "columnName":"",//显示的列的名称(一行中第一个选项)
    "titleStyle":"",  //每列标题样式
    "titleClassName":"",  //每列标题class
    "columnClassName":"",  //每列数据class
    "columnStyle":""    //每列数据样式
    "callback":event   //列内容转换回调;返回三个值,显示列的名称,显示列的ID ,当前行的数据对象

    "last":true      //看操作的选项是显示在一行的前面还是后面
  }*/
  this.onCheckbox = options.onCheckbox;   //选择框改变时,调用的方法
  this.info_templates = options.info_templates || "";    //列表头部显示的模板html
  this.page = options.page;

  //分页信息page = {count:100,总条数,pageSize:30,每页显示条数,goPageCallback,上一页下一页回调}
  this._pageIndex = 0;   //对内使用,当前页
  this.pageIndex = -1;    //对外使用,可以用它来设置当前页的值
  this._pageCount = 1;     //对内使用,总共有多少页
  this.selectBtnType = options.selectBtnType || "checkbox";  //选择框类型,单选还是多选,默认多选
  this.element = $("#" + this.bingContorlId);    //包装列表html的元素
  this.checkButtonCallback = options.checkButtonCallback || null;//单选框(或多选框)的回调,用来判断该行是否需要显示这个单选框(或多选框);
  this.ListHash = {};  //每行的数据对象的Hash,主键为key的值,假设key为id,那么json的id的值就是主键

  this.create();     //创建列表

}

List.prototype = {
  getSelectValue: function (flag) {
    var selectID = "";
    $("[name='" + this.key + "']").each(function () {
      if ($(this).attr("checked")) {      //如果一行的选择框被选,就取它的值,组装成1,2,3....这种格式的字符串
        selectID += (flag ? $(this).data("value") : $(this).val()) + ","
      }
    });
    selectID = selectID.length > 0 ? selectID.substr(0, selectID.length - 1) : "";   //去掉最后一个","字符,返回
    return selectID;
  },
  
  setBody: function (html) {
    this.body.html(html || ""); 
    if (this.page) {     //如果有页码,先生成页码
      this.body.append(this.getPaginationHtml());   //添加页码的html到显示数据列表的div元素中
    }
  },
  setInfo: function (data, info_templates) {   
    if (this.info.length == 0) {    //如果没有p,就返回不做任何处理,jQuery对象的length属性
      return;
    }
    this.info.html(info_templates || this.info_templates);   //否则就把用户传入的内容,放到列表项的最前面,用p元素显示
  },
  create: function () {
    this.element.empty();    //先把容器里面的html清空。
    this.wrap = $(this.getHeaderHtml()).appendTo(this.element);   //把列表的标题先生成,然后添加到容器中

    this.body = this.wrap.find(".table-body");   //找到显示数据列表的div元素
    this.info = this.wrap.filter(".table-info");   //过滤出显示用户传入的信息的p元素
    this.setBody();      //把数据生成列表
    this.setInfo();
  },
  getHeaderHtml: function () {
    var htmlTemp = [];
    htmlTemp.push(this.info_templates ? '<p class="pt_5 pb_5 table-info"></p>' : "");    //是否有传入头部模板html,有,就在列表前面加入一个p标签
    htmlTemp.push(' <div class="tablewrap" > <table cellspacing="0" class="tb_head"><tr>');    //table标签
    if (this.selectBtnType == "radio") {     //如果显示的是单选项标签,就表示第一行的第一个td不用添加input。如果是多选,需要点击时,把后面所有的行都选择上。
      htmlTemp.push('<td class="cb"></td>');      //就在行的前面添加一个td标签。
    }
    else if (this.isCheckButton) {    //如果不是单选,而是多选,就看是否支持点击第一行的第一个td,就选择后面所有的行。
      htmlTemp.push('<td class="cb"><input class="checkAll" type="checkbox" name="" /></td>');
    }
    for (var i = 0; i < this.columns.length; i++) {    //循环columns,看要显示多少个选项。
      !this.columns[i].last && htmlTemp.push('<td ' + (this.columns[i].titleStyle ? "style='" + this.columns[i].titleStyle + "'" : "") + '><div>' +         this.columns[i].columnName + '</div></td>');
    }
    if (this.operateList && this.operateList.length > 0) {   //如果有操作选项,就显示操作的选项。
      htmlTemp.push('<td ><div>' + "操作" + '</div></td>');
    }
    for (var i = 0; i < this.columns.length; i++) {
      this.columns[i].last && htmlTemp.push('<td ' + (this.columns[i].titleStyle ? "style='" + this.columns[i].titleStyle + "'" : "") + '><div>' +       this.columns[i].columnName + '</div></td>');
    }
    htmlTemp.push('</tr></table>');
    htmlTemp.push('<div class="table-body"></div></div>');     //返回:<p></p><div><table><tr></tr></table><div></div></div>,其中p是显示传入的信息的,一般用来当做列表的提示信息。第一个div是列表的最外层,table是列表的第一行,也就是列表的标题,第二个div是用来显示数据的列表的
    return htmlTemp.join("");
  },
  getPaginationHtml: function () {    //得到页码的html
    var page = this.page;
    this._pageCount = Math.ceil(page.count / page.pageSize);     //总数除以一页显示的数量
    if (this._pageCount <= 1) {    //如果只有一页,就不用显示页码
      return "";
    }
    if (this.pageIndex >= 0) {   //默认为-1
      this._pageIndex = this.pageIndex;    
    }
    var pageIndex = this._pageIndex + 1;     //_pageIndex 默认为0
    var p = this;
    var html = '<div class="pop_pager p_relative ta_r">

      <div style="left:auto; *top:-30px; right:10px;display:none;" class="pager-pop">'  

      //这个div是用户点击下面的页码(比如1/6)标识时弹出来让用户选择去到第几页的框
+ '跳转到第'+ '<input id="txt_pageIndex" type="text" class="pager_txt col_black">'+ ' 页<a class="lit_btn_on" href="javascript:void(0);" id="btnGoPage">' + "确定" + '</a> </div>'
+ '<a href="javascript:void(0);" ' + (pageIndex == 1 ? 'class="col_grey"' : '') + 'id="a_lastPage">' + "上一页" + '</a>'  

//如果第一页,那么上一页就为灰色,代表不能点击
+ '<a href="javascript:void(0);" class="pop_pager_num"><span>' + pageIndex + '/' + this._pageCount + '</span><i class="s-down ml_5"></i></a>'
+ '<a href="javascript:void(0);" ' + (this._pageCount <= pageIndex ? 'class="col_grey"' : '') + ' id="a_nextPage">' + "下一页" + '</a></div>';
    return html;
  },
  getHtmlList: function (data) {
    var p = this;
    var htmlTemp = [];
    this.dataResoure = data || this.dataResoure;
    if (this.dataResoure) {
      for (var i = 0; i < this.dataResoure.length; i++) {
        var rows = this.dataResoure[i];    //json数据:{id:1,name:"chaojidao",age:25}
        if (!p.ListHash[rows[p.key]]) {    //假设key为id,rows[id] = 1,ListHash[1] = undefined
          p.ListHash[rows[p.key]] = rows;      p.ListHash = {1:{id:1,name:"chaojidao",age:25}},
        }
        htmlTemp.push('<div data-role="li" class="li-data-div" data-key="' + rows[p.key] + '"><table cellspacing="0" class="tb_comb"><tr>');
        if (this.isCheckButton) {
          var showCheckButton = true;//判断当前行是否需要显示选择框,默认显示;
          if (this.checkButtonCallback && !this.checkButtonCallback(rows))  //如果这条json数据,传进去返回了false,就证明不显示这个多选框
            showCheckButton = false;
          htmlTemp.push('<td class="cb">');
          htmlTemp.push(showCheckButton ? '<input type="' + (this.selectBtnType ? this.selectBtnType : 'checkbox') + '" data-value="' + (rows["id"] ? rows["id"] : "") + '" name="' + this.key + '" value="' + rows[this.key] + '" ' : '');
          if (this.chkBingColumn && showCheckButton) {
            for (var z = 0; z < this.chkBingColumn.length; z++) {
              htmlTemp.push(this.chkBingColumn[z] + '="' + rows[this.chkBingColumn[z]] + '"');
            }
          }
          htmlTemp.push(showCheckButton ? '/>' : '');
          htmlTemp.push('</td>');
         }
        for (var j = 0; j < this.columns.length; j++) {
          var col = this.columns[j];
          var sTitle = "";
          if(rows[col.columnId]==null){        //col.columnId=id,rows[col.columnId] = 1
            sTitle = "";
          }else{
            sTitle = rows[col.columnId].toString();
          }
          if(!col.last){
            htmlTemp.push('<td ' + (col.columnClassName ? "class='" + col.columnClassName + "'" : "") + (col.titleStyle ? "style='" + col.titleStyle + "'" : "") + '><div class="tf" ' + (col.showTitle ? " title='" +sTitle + "'" : "") + '>');  

             //如果要显示title,就显示值(因为值可能比较长,有tilte形式显示)
            htmlTemp.push(col.callback ? col.callback.call(p, rows[col.columnId], col.columnId, rows) : rows[col.columnId]);

            //如果有回调方法,就显示回调方法返回的值
            htmlTemp.push('</div></td>');
          }
        }

        if (this.operateList && this.operateList.length > 0) {
          htmlTemp.push('<td ><div>');
          var operateIndex = 0;
          for (var z = 0; z < this.operateList.length; z++) {
            var btnObj = this.operateList[z];
            var changeText = btnObj.text;
            if (btnObj.changeText) {
              changeText = btnObj.changeText(rows);
              if (changeText == "") {
                 continue;
              }
            }
            htmlTemp.push('<a class="udline ' + (operateIndex != 0 ? "ml_5" : "") + '"' + (btnObj.action ? 'data-action="' + btnObj.action + '"' : '') +' data-index=' +z +' data-key="' +rows[this.key] +'" href="###" ');

            if (btnObj.bingColums && btnObj.bingColums.length > 0) {   //如果要操作多个属性值,需要把这些值先放在操作的a链接的属性上
              for (var j = 0; j < btnObj.bingColums.length; j++) {
                htmlTemp.push(btnObj.bingColums[j] + '="' + rows[btnObj.bingColums[j]] + '" ');
              }

            }
            operateIndex++;
            htmlTemp.push(">");
            htmlTemp.push(changeText)
            htmlTemp.push('</a>');

          }

          htmlTemp.push("</div></td>");
        }

        for (var j = 0; j < this.columns.length; j++) {
          var col = this.columns[j];
          if (col.last) {
            htmlTemp.push('<td ' + (col.columnClassName ? "class='" + col.columnClassName + "'" : "") + (col.titleStyle ? "style='" + col.titleStyle + "'" : "") + '><div class="tf" ' + (col.showTitle ? " title='" + rows[col.columnId].toString()+ "'" : "") + '>');
            htmlTemp.push(col.callback ? col.callback.call(p, rows[col.columnId], col.columnId, rows) : rows[col.columnId]);
            htmlTemp.push('</div></td>');
          }
        }
        htmlTemp.push("</tr></table></div>");
      }
    }
    return htmlTemp.join("");
  },

  getSingData: function (id) {     //得到单条数据的值,json对象
    return this.ListHash[id];
  },

  onOperatecik: function (event) {
    var target = event.target;
    if (target.nodeName == "A" && target.getAttribute("data-index")) {  

      //只有操作按钮才会进入if语句,getAttribute得到的是字符串,也就是"0"也是true,但是数字0就会返回false。
      var index = parseInt(target.getAttribute("data-index"));
      var btn = this.operateList[index];
      key = target.getAttribute("data-key");   //key属性id的值
      if (btn.callback)
        callback = btn.callback;
      
      if (btn.bingColums) {   //如果要操作一行中的多个属性值,就传入bingColums数组,数组就代表你要操作的多个属性的名字
        backObj = {"id": key};
        for (var z = 0; z < btn.bingColums.length; z++) {
          backObj[btn.bingColums[z]] = target.getAttribute(btn.bingColums[z]);
        }
        callback.call(this, backObj);    //就会传入回调方法,多个属性值
      }
      else {
        callback && callback.call(this, key);    //只传入回调方法一个值:key的值
      }
    }
  },
  dataBind: function (data) {    //把数据生成列表
    var p = this;
    this.dataResoure = data;
    var html = p.getHtmlList();
    html = html.length > 0 ? html : "";//暂无数据
    p.setBody(html);
    p._bindEvent();
  },
  _bindEvent: function () {
    var p = this;
    this.element.click($.proxy(this.onOperatecik, this));  

    //事件委托机制,点击this.element中的子元素,就会调用onOperatecik方法,但是这个方法会做判断,只有操作按钮才会进行响应。
    var call = $(".checkAll").removeAttr("checked"),//先把标题行的选择框的checked属性去除,也就是默认不会选择,单选框这里不会取到元素

      chk = $("[name='" + p.key + "']");   //取到列表的所有行的选择框的元素
    call.bind("change", function () {     //改变标题行的选择框是否选择时执行的方法,单选框时,这里绑定无效
      var th = this;
      for (var i = 0, l = chk.length; i < l; i++) {
        chk[i].checked = th.checked;      //只要标题行的选择框有变化,就会改变列表的所有行的选择框的元素
      }
      p.onCheckbox && p.onCheckbox.call(this, p.getSelectValue());   //调用用户传入的回调方法,并把选择的值传进这个回调方法
    });
    if (this.selectBtnType == "checkbox") {   //如果是多选框,就绑定列表的每一行的选择框的change事件。
      chk.bind("change", function () {
        call[0].checked = (chk.length == chk.filter(":checked").length);    //如果列表的每行都选择上了,那么标题的选择框也会选择上。
        p.onCheckbox && p.onCheckbox.call(this, p.getSelectValue());   
      });
    }

    $("#a_lastPage").click(function () {   //上一页点击
        if (p._pageIndex <= 0) {
          return;
        }
        else {
          p._pageIndex = p._pageIndex - 1;
          $("#contentDiv", parent.document).scrollTop(0);   //由于上一页或下一页在页面的最底下,存在滚动条的问题,所以点击上一页或下一页后需要滚动到页面的最上面。contentDiv是装此列表的div的id。parent.document处理可能存在iframe的情况。
          p.page.goPageCallback(p._pageIndex, p._pageIndex * 30);   //调用用户传入的去哪一页的回调方法
        }
        return false;
    });
    $("#a_nextPage").click(function () {   //下一页点击

      if (p._pageIndex >= p._pageCount - 1) {
        return;
      }
      else {
        p._pageIndex = p._pageIndex + 1;
        $("#contentDiv", parent.document).scrollTop(0);
        p.page.goPageCallback(p._pageIndex, p._pageIndex * 30);
      }
      return false;
    });
    $("#btnGoPage").click(function () {    //去哪一页点击
      var index = $("#txt_pageIndex").val();
      if (index > p._pageCount || index < 1 || !(/^d+$/).test(index)) {
        return;
      }
      p._pageIndex = parseInt(index) - 1;
      $("#contentDiv", parent.document).scrollTop(0);
      p.page.goPageCallback(p._pageIndex, p._pageIndex * 30);
      return false;
    });
    $(".pop_pager_num").click(function () {   //弹出去哪一页的框
      $(document).unbind("click", _click).bind("click", _click);
      function _click(e) {
        var target = $(e.target);
        if (target.closest(".pop_pager").length == 0) {
          $(".pager-pop").hide();
        }
      }

      $(".pager-pop").show();
      return false;
    });
    $("#txt_pageIndex").keyup(function (event) {    //绑定键盘的enter事件,效果也是去哪一页
      if (event.keyCode == 13) {
        $("#btnGoPage").click();
        return false;
      }
    });
    $(".tb_comb").mouseover(function () {   //鼠标移到哪一行,背景颜色变化
      $(this).css("background", "#ecf1f6");
    });
    $(".tb_comb").mouseout(function () {
      $(this).css("background", "");
    });
  }
}

使用此插件,需要先

var list = new List({

    "columns":[                                                 //假设json = {id:1,name:"chaojidan",age:25}

      { "showTitle":true,     //把id的值赋给id选项的title属性

        "columnId": "id",      //显示json对象数据的属性名

        "columnName":"ID"     //对应列表中的一个选项名

        "callback":function(value,key,json){ return }   //选项的名字不再是ID,而是返回的值

      },

      {        

        "columnId": "name",     //显示json对象数据的属性名

        "columnName":"姓名"   //对应列表中的一个选项名

      },

      {

        "columnId": "age",    //显示json对象数据的属性名

        "columnName":"年龄"   //对应列表中的一个选项名

      }

    ],    //就会把上面json的数据显示在列表中,列表中的每一行就是:  ID       姓名       年龄   

    "operateList": [     //操作按钮
      {
        "text": "跳转到其他页面",    

        "changeText":function(json){   return    },   //如果返回"",就不会显示这个操作链接
        "callback": function (id) {
          document.location.href = 'dan.do?sid=' + parent.gMain.sid + '&func=vas:chao&ji=' + id;
        },

        "bingColums":["name","age"],
      },
      {
        "text": "删除",
        "callback": function (id) {

          //删除此列表
        }
      }
    ],

    "key":id,

    "isCheckButton":true,   //是否有多选框
    "info_templates":"牛B的列表" ,   //在列表最前面显示的
    "bingContorlId": "listContent",    //页面上的div元素的id,此div用来显示list列表的   

    "onCheckbox": function (result) {}    //列表中的多选框,选择时,会调用此方法,result是已经选择的选项值

})

list.page = {
  "count": data.listCount,    //要显示的数据的总条数
  "pageSize": 30,    //一页显示多少条数据
  "goPageCallback": goPage      //上一页,下一页,去什么页,调用的方法
};
list.dataBind(data.obj);   //把数据传入此方法,然后生成列表,只有调用了此方法,才会显示数据列表,不然就只会显示有标题的列表。此方法会设置列表的内容,和列表的页码,不会设置列表的头内容。

goPage方法,就会用传进来的index(要去第几页),去后台取数据,取到数据后,重新调用dataBind方法。就可以再次显示新的数据了。

要想得到选择的列表的选项,可以使用list.getSelectValue();得到当前列表选中的行的所有key,用","分隔开。

list.getSingData(id),得到列表中一行的属性值,也就是其中一个json对象。通过它的key(id)的值。

加油!

原文地址:https://www.cnblogs.com/chaojidan/p/4158365.html