无刷新分页相信大家也看过不少,包括一些Jquery的分页插件。之前做产品分页的时候也下载研究过几个,但是用现在的话说就是硬伤很多,不说代码太多以及和自己数据库很难对接,从SEO的角度考虑(必须要有有质量的链接),实在是就没办法用了。于是就决定自己写了一个,一方面本人确实也称不上高手,还是那句话,贴出来供大家参考,另一方面,文采很差,所以尽量少说话,多解释代码。
说明
无刷新的分页主要分为三个步骤
步骤 A:数据库分页查询
方法A1
方法A2
步骤B:后台分页处理
步骤B1
步骤B2
步骤C:前台分页调用。
步骤C1
步骤C2
惯例结尾会把实例附上。
步骤A :数据库分页查询
这里提供两种方法
方法A1:SQL SERVER自带的分页查询语法
用我们这边的产品数据库做实例:查询产品表Product中产品名(ProductName)中带S的的第一页数据,限制每页个数为10,代码:
declare @pageIndex int--页数索引 declare @keyWord varchar(50)--搜索关键词 declare @pageCount int--每页数据个数 set @pageIndex=1 set @keyWord='S' set @pageCount=10; with Row as(SELECT row_number() over( order by addtime) as Num,AddTime,ProductName from Product where ProductName like '%'+@keyWord+'%' ) select top 10 * from Row where Num>(@pageIndex-1) * @pageCount and Num<= @pageIndex* @pageCount
结果返回一个DatatTable。这是一种比较简单的方法,代码也比较少,当然,支持的功能也有限,如果只是想简单的实现分页功能,可以考虑一下。
方法A2:SQL SERVER分页存储过程
此存储过程是我在网上搜到的,具体地址我也记不清了,刚才在百度上搜了一下竟然没搜到,在这里只能向作者说声抱歉,贴一下你的代码,如果有人能找到作者的原贴请留言发下地址,我会及时贴上去的。

CREATE PROC [dbo].[P_viewPage] @TableName VARCHAR(200), --表名 @FieldList VARCHAR(2000), --显示列名,如果是全部字段则为* @PrimaryKey VARCHAR(100), --单一主键或唯一值键 @Where VARCHAR(2000), --查询条件不含'where'字符,如id>10 and len(userid)>9 @Order VARCHAR(1000), --排序不含'order by'字符,如id asc,userid desc,必须指定asc或desc --注意当@SortType=3时生效,记住一定要在最后加上主键,否则会让你比较郁闷 @SortType INT, --排序规则1:正序asc 2:倒序desc 3:多列排序方法 @RecorderCount INT, --记录总数0:会返回总记录 @PageSize INT, --每页输出的记录数 @PageIndex INT, --当前页数 @TotalCount INT OUTPUT, --记返回总记录 @TotalPageCount INT OUTPUT --返回总页数 AS SET NOCOUNT ON IF ISNULL(@TotalCount,'') = '' SET @TotalCount = 0 SET @Order = RTRIM(LTRIM(@Order)) SET @PrimaryKey = RTRIM(LTRIM(@PrimaryKey)) SET @FieldList = REPLACE(RTRIM(LTRIM(@FieldList)),' ','') WHILE CHARINDEX(', ',@Order) > 0 OR CHARINDEX(' ,',@Order) > 0 BEGIN SET @Order = REPLACE(@Order,', ',',') SET @Order = REPLACE(@Order,' ,',',') END IF ISNULL(@TableName,'') = '' OR ISNULL(@FieldList,'') = '' OR ISNULL(@PrimaryKey,'') = '' OR @SortType < 1 OR @SortType >3 OR @RecorderCount < 0 OR @PageSize < 0 OR @PageIndex < 0 BEGIN PRINT('ERR_00') RETURN END IF @SortType = 3 BEGIN IF (UPPER(RIGHT(@Order,4))!=' ASC' AND UPPER(RIGHT(@Order,5))!=' DESC') BEGIN PRINT('ERR_02') RETURN END END DECLARE @new_where1 VARCHAR(1000) DECLARE @new_where2 VARCHAR(1000) DECLARE @new_order1 VARCHAR(1000) DECLARE @new_order2 VARCHAR(1000) DECLARE @new_order3 VARCHAR(1000) DECLARE @Sql VARCHAR(8000) DECLARE @SqlCount NVARCHAR(4000) IF ISNULL(@where,'') = '' BEGIN SET @new_where1 = ' ' SET @new_where2 = ' WHERE ' END ELSE BEGIN SET @new_where1 = ' WHERE ' + @where SET @new_where2 = ' WHERE ' + @where + ' AND ' END IF ISNULL(@order,'') = '' OR @SortType = 1 OR @SortType = 2 BEGIN IF @SortType = 1 BEGIN SET @new_order1 = ' ORDER BY ' + @PrimaryKey + ' ASC' SET @new_order2 = ' ORDER BY ' + @PrimaryKey + ' DESC' END IF @SortType = 2 BEGIN SET @new_order1 = ' ORDER BY ' + @PrimaryKey + ' DESC' SET @new_order2 = ' ORDER BY ' + @PrimaryKey + ' ASC' END END ELSE BEGIN SET @new_order1 = ' ORDER BY ' + @Order END IF @SortType = 3 AND CHARINDEX(','+@PrimaryKey+' ',','+@Order)>0 BEGIN SET @new_order1 = ' ORDER BY ' + @Order SET @new_order2 = @Order + ',' SET @new_order2 = REPLACE(REPLACE(@new_order2,'ASC,','{ASC},'),'DESC,','{DESC},') SET @new_order2 = REPLACE(REPLACE(@new_order2,'{ASC},','DESC,'),'{DESC},','ASC,') SET @new_order2 = ' ORDER BY ' + SUBSTRING(@new_order2,1,LEN(@new_order2)-1) IF @FieldList <> '*' BEGIN SET @new_order3 = REPLACE(REPLACE(@Order + ',','ASC,',','),'DESC,',',') SET @FieldList = ',' + @FieldList WHILE CHARINDEX(',',@new_order3)>0 BEGIN IF CHARINDEX(SUBSTRING(','+@new_order3,1,CHARINDEX(',',@new_order3)),','+@FieldList+',')>0 BEGIN SET @FieldList = @FieldList + ',' + SUBSTRING(@new_order3,1,CHARINDEX(',',@new_order3)) END SET @new_order3 = SUBSTRING(@new_order3,CHARINDEX(',',@new_order3)+1,LEN(@new_order3)) END SET @FieldList = SUBSTRING(@FieldList,2,LEN(@FieldList)) END END SET @SqlCount = 'SELECT @TotalCount=COUNT(*),@TotalPageCount=CEILING((COUNT(*)+0.0)/' + CAST(@PageSize AS VARCHAR)+') FROM ' + @TableName + @new_where1 IF @RecorderCount = 0 BEGIN EXEC SP_EXECUTESQL @SqlCount,N'@TotalCount INT OUTPUT,@TotalPageCount INT OUTPUT', @TotalCount OUTPUT,@TotalPageCount OUTPUT END ELSE BEGIN SELECT @TotalCount = @RecorderCount END IF @PageIndex > CEILING((@TotalCount+0.0)/@PageSize) BEGIN SET @PageIndex = CEILING((@TotalCount+0.0)/@PageSize) END IF @PageIndex = 1 OR @PageIndex >= CEILING((@TotalCount+0.0)/@PageSize) BEGIN IF @PageIndex = 1 --返回第一页数据 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where1 + @new_order1 END IF @PageIndex >= CEILING((@TotalCount+0.0)/@PageSize) --返回最后一页数据 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM (' + 'SELECT TOP ' + STR(ABS(@PageSize*@PageIndex-@TotalCount-@PageSize)) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where1 + @new_order2 + ' ) AS TMP ' + @new_order1 END END ELSE BEGIN IF @SortType = 1 --仅主键正序排序 BEGIN IF @PageIndex <= CEILING((@TotalCount+0.0)/@PageSize)/2 --正向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where2 + @PrimaryKey + ' > ' + '(SELECT MAX(' + @PrimaryKey + ') FROM (SELECT TOP ' + STR(@PageSize*(@PageIndex-1)) + ' ' + @PrimaryKey + ' FROM ' + @TableName + @new_where1 + @new_order1 +' ) AS TMP) '+ @new_order1 END ELSE --反向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM (' + 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where2 + @PrimaryKey + ' < ' + '(SELECT MIN(' + @PrimaryKey + ') FROM (SELECT TOP ' + STR(@TotalCount-@PageSize*@PageIndex) + ' ' + @PrimaryKey + ' FROM ' + @TableName + @new_where1 + @new_order2 +' ) AS TMP) '+ @new_order2 + ' ) AS TMP ' + @new_order1 END END IF @SortType = 2 --仅主键反序排序 BEGIN IF @PageIndex <= CEILING((@TotalCount+0.0)/@PageSize)/2 --正向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where2 + @PrimaryKey + ' < ' + '(SELECT MIN(' + @PrimaryKey + ') FROM (SELECT TOP ' + STR(@PageSize*(@PageIndex-1)) + ' ' + @PrimaryKey +' FROM '+ @TableName + @new_where1 + @new_order1 + ') AS TMP) '+ @new_order1 END ELSE --反向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM (' + 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where2 + @PrimaryKey + ' > ' + '(SELECT MAX(' + @PrimaryKey + ') FROM (SELECT TOP ' + STR(@TotalCount-@PageSize*@PageIndex) + ' ' + @PrimaryKey + ' FROM ' + @TableName + @new_where1 + @new_order2 +' ) AS TMP) '+ @new_order2 + ' ) AS TMP ' + @new_order1 END END IF @SortType = 3 --多列排序,必须包含主键,且放置最后,否则不处理 BEGIN IF CHARINDEX(',' + @PrimaryKey + ' ',',' + @Order) = 0 BEGIN PRINT('ERR_02') RETURN END IF @PageIndex <= CEILING((@TotalCount+0.0)/@PageSize)/2 --正向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ( ' + 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ( ' + ' SELECT TOP ' + STR(@PageSize*@PageIndex) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where1 + @new_order1 + ' ) AS TMP ' + @new_order2 + ' ) AS TMP ' + @new_order1 END ELSE --反向检索 BEGIN SET @Sql = 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ( ' + 'SELECT TOP ' + STR(@PageSize) + ' ' + @FieldList + ' FROM ( ' + ' SELECT TOP ' + STR(@TotalCount-@PageSize*@PageIndex+@PageSize) + ' ' + @FieldList + ' FROM ' + @TableName + @new_where1 + @new_order2 + ' ) AS TMP ' + @new_order1 + ' ) AS TMP ' + @new_order1 END END END PRINT(@Sql) EXEC(@Sql)
各个参数作者都做了解释,只要给各个参数赋值,执行存储过程就可以得到一个相应的Datatable。
步骤B:后台分页处理
分页处理方法也就是前台ajax调用的方法,有两个参数,参数PageIndex int类型,为当前页数;参数key,搜索产品关键词。方法返回List<String>类型,方法声明代码(.NET):
[WebMethod] public static List<String> LoadProduct(int PageIndex, String key) { List<String> list = new List<String>(); //.. return list; }
实例中用的数据库查询方法是A2方法SQL SERVER分页存储过程,本人亲测百万条数据情况下执行这个存储过程没什么压力。在分页处理方法中声明参数的代码:
string TableName = "VProducts";//表名 string FieldList = "Pictures,ProductName,IDPlus,addtime";//字段集合 string PrimaryKey = "IDPlus";//主键 string Order = "IDPlus desc";//排序 int SortType = 2;//排序规则 1:正序asc 2:倒序desc 3:多列排序方法 int RecorderCount = 0;//字段集合 int PageSize = 4;//每页输出的记录数 String where = "ProductName like '%" + key + "%'"; SqlParameter[] paras = { new SqlParameter("@TableName",TableName), new SqlParameter("@FieldList",FieldList), new SqlParameter("@TotalPageCount",SqlDbType.Int), new SqlParameter("@TotalCount",SqlDbType.Int), new SqlParameter("@PrimaryKey",PrimaryKey), new SqlParameter("@Where",where), new SqlParameter("@Order",Order), new SqlParameter("@SortType",SortType), new SqlParameter("@RecorderCount",RecorderCount), new SqlParameter("@PageSize",PageSize), new SqlParameter("@PageIndex",PageIndex) }; paras[2].Direction = ParameterDirection.InputOutput;//设置 paras[2].Value = 0; paras[3].Direction = ParameterDirection.InputOutput;//设置 paras[3].Value = 0; DataTable DT = DBHelper.GetDataTable("P_viewPage", paras);//执行存储过程得到Datatable
通过执行存储过程得到一个DataTable。之后要进行两个步骤操作。
步骤B1 遍历这个datatable,然后填充到一个HTML模板里。
这个HTML模板就是显示单条产品的HTML元素,如实例中把每个产品显示在一个li里
String Temp = @"<li class='last'> <div class='pic'> <a href='javascript:void(0)'> <img width='160' src='{0}' /></a> </div> <div class='name'> <a href='javascript:void(0)'>{1}</a> </div> </li>";
然后就可以填充了,代码:
StringBuilder SB = new StringBuilder();//建议用StringBuilder int PageNum = Convert.ToInt32(paras[2].Value);//输出参数 总页数 int ProductCount = Convert.ToInt32(paras[3].Value);//输出参数 总个数 if (DT != null && DT.Rows.Count != 0) { for (int i = 0; i < DT.Rows.Count; i++) { SB.AppendFormat(Temp, DT.Rows[i]["Pictures"].ToString(), DT.Rows[i]["ProductName"].ToString()); } }
存储过程还有两个输出参数,分别记录总页数和总个数。
步骤B2
步骤B1已经得到了该页数的所有数据,步骤B2是关于页数的操作,如图:
这一块的代码要根据当前页数及总页数生产,并且为每页分配一个可用连接,当在浏览器中输入该连接,会加载该页的产品。代码:

StringBuilder SBPage = new StringBuilder(); if (PageNum != 0) { #region 分页显示 string path = System.Web.HttpContext.Current.Request.UrlReferrer + (key != "" ? "&" : "?") + "pageindex=";//判断是否有参数key string strFirstTemp = "<li><a onclick='return getList(1);' href='" + path + "1'>首页</a></li>";//拼接url string strLastTemp = "<li><a onclick='return getList(" + (PageNum).ToString() + ");' href='" + path + (PageNum).ToString() + "' >尾页</a></li>"; string strFrontTemp = "<li><a onclick='return getList(" + (PageIndex - 1).ToString() + ");' href='" + path + (PageIndex - 1).ToString() + "' >上一页</a></li>"; string strNextTemp = "<li><a onclick='return getList(" + (PageIndex + 1).ToString() + ");' href='" + path + (PageIndex + 1).ToString() + "' >下一页</a></li>"; string strCurrentTemp = "<li><span >{0}</span></li>"; string strPageTemp = "<li><a onclick='return getList({0});' href='" + path + "{0}'>{0}</a></li>"; //若页数=1 if (PageNum == 1) { SBPage.AppendFormat(strCurrentTemp, 1); } else//若页数大于1 { if (PageIndex == 1)//若当前页=1 { SBPage.AppendFormat(strCurrentTemp, 1); if (PageNum <= 10)//若总页数小于等于10 { for (int i = 2; i <= PageNum; i++) { SBPage.AppendFormat(strPageTemp, i); } } else//若总页数大于10 { for (int i = 2; i <= 10; i++) { SBPage.AppendFormat(strPageTemp, i); } } SBPage.Append(strNextTemp); SBPage.Append(strLastTemp); } else if (PageIndex == PageNum)//若当前页等于总页数 { SBPage.Append(strFirstTemp); SBPage.Append(strFrontTemp); if (PageNum <= 10)//若总页数小于等于10 { for (int i = 1; i < PageNum; i++) { SBPage.AppendFormat(strPageTemp, i); } } else//若总页数大于10 { for (int i = (PageNum - 9); i < PageNum; i++) { SBPage.AppendFormat(strPageTemp, i); } } SBPage.AppendFormat(strCurrentTemp, PageNum); } else//若当前页数不等于1且不等于总页数 { if (1 < PageNum && PageNum <= 10)//若总页数大于1小于等于10 { SBPage.Append(strFirstTemp); SBPage.Append(strFrontTemp); for (int i = 1; i <= PageNum; i++) { if (PageIndex == i) { SBPage.AppendFormat(strCurrentTemp, i); } else { SBPage.AppendFormat(strPageTemp, i); } } SBPage.Append(strNextTemp); SBPage.Append(strLastTemp); } else//若总页数大于10 { if (1 < PageIndex && PageIndex <= 5)//若当前页数大于1且小于等于5 { SBPage.Append(strFirstTemp); SBPage.Append(strFrontTemp); for (int i = 1; i <= 10; i++) { if (PageIndex == i) { SBPage.AppendFormat(strCurrentTemp, i); } else { SBPage.AppendFormat(strPageTemp, i); } } SBPage.Append(strNextTemp); SBPage.Append(strLastTemp); } else//若当前页数大于5 { if (5 < PageIndex && PageIndex <= (PageNum - 5))//若当前页数大于5且小于总页数-5 { SBPage.Append(strFirstTemp); SBPage.Append(strFrontTemp); for (int i = (PageIndex - 4); i <= (PageIndex + 5); i++) { if (PageIndex == i) { SBPage.AppendFormat(strCurrentTemp, i); } else { SBPage.AppendFormat(strPageTemp, i); } } SBPage.Append(strNextTemp); SBPage.Append(strLastTemp); } else//若当前页数小于总页数-5 { SBPage.Append(strFirstTemp); SBPage.Append(strFrontTemp); for (int i = (PageNum - 9); i <= PageNum; i++) { if (PageIndex == i) { SBPage.AppendFormat(strCurrentTemp, i); } else { SBPage.AppendFormat(strPageTemp, i); } } SBPage.Append(strNextTemp); SBPage.Append(strLastTemp); } } } } } #endregion }
最后把得到的各种数据添加到声明的list里,并返回给前台JS处理
list.Add(SB.ToString()); list.Add(SBPage.ToString()); list.Add(PageNum.ToString()); list.Add(ProductCount.ToString()); return list;
步骤C:前台分页调用
同步步骤B2中的代码已经看到返回的HTML元素中已经有了onclick事件,调用的是前台方法getList
我们来看看这个方法的代码:
function getList(pageIndex) { $.ajax({ type: "POST", url: "/cnblog/ajax.aspx/LoadProduct", data: "{'PageIndex':'" + pageindex + "','key':'" + decodeURIComponent($.request("key")) + "'}", //搜索关键字,如果没有则为空 contentType: "application/json; charset=utf-8", dataType: "json", success: function (result) { $(".productslist").html(result.d[0]); $(".paginator").html(result.d[1]); $(".pageCount").html(result.d[2]); $(".productCount").html(result.d[3]); }, error: function (result) { } }); return false;//一定要加return false。在ie里一个超链接有onclick事件也有href属性,如果不加return false 点击的时候会触发事件后再跳转 }
参数为pageIndex ,既页数 ,所以当点击返回的页数连接时会调用这个方法,并且把页数及搜索关键词传过去,传过去之后得到的结果即为步骤B中LoadProduct方法的返回值。
我们来看看效果吧!
注意截图上的文字解释: