如何为许多页面做页面导航? 对数页面导航
为许多页面显示页面导航的最佳方式是什么?
(最初,这是作为一个方法提示,我的答案包括在问题中,现在我把答案分解到下面的“答案”部分)。
更具体地说:
假设您向用户显示一组logging,将其分解为固定大小的页面(例如,Googlesearch的结果)。 如果只有几页,则可以在结果的末尾显示一个页面导航区域,如下所示:
[<<] [<] 1 2 3 4 5 6 7 8 9 10 11 12 13 [>] [>>]
但是,如果结果超过20或30页,这很快就会变得不正常。
有时你会看到这样的事情:
[<<] [<] … 665 666 667 668 669 670 671 672 673 … [>] [>>]
或这个:
[<<] [<] 1 2 3 … 667 668 669 670 671 … 845 846 847 [>] [>>]
但在这两种情况下,导航到“…”部分中间的任何地方都需要很多很多的mousclicks。 有时会提供直接input页码的input框, 否则(假设我们在这里讨论的是一个网页),精明的用户可能会查看url,看看他们是否可以直接编辑它。
最好是有一个分页显示,让用户只需几个鼠标点击即可到达任何页面,而不会有太多可怕的链接。
那最好如何实现?
这是我的解决scheme – 使用“对数页面导航”:
这可以通过使页码按对数分布来实现,根据离端点或当前页面的距离。 这里是我的意思的一个例子:
1 2 3 4 5 6 10。 20。 30。 40。 50 .. 100 .. 200。 210。 220。 230。 240。 250。 252 253 254 255 256 257 258 259 260 261 262。 270。 280。 290。 300。 310 .. 400 .. 500 .. 600 .. 700 .. 800 .. 900 .. 950。 960。 970。 980。 990。 995 996 997 998 999 1000
请注意,如何在空白处编号从1s到10s到100s(等)。 (我使用10的权力,但原则上你可以使用不同的scheme – 权力2,说)。
我在2004年写了一些代码,并认为我会在这里分享。 有PHP和ASP版本,但逻辑应该是简单的翻译成任何语言。 请注意,底部的位(在这两种情况下)只是显示一些示例。 显然格式化将需要定制,以匹配您的网页(或应用程序),所以这是非常基本的。 LINKS_PER_STEP
paginationHTML
LINKS_PER_STEP
,以确定在步长增加之前显示的数字数量,当您离开端点或当前页面时。
为了更紧凑的输出,你也可以考虑改变代码,使得编号在端点周围不是“密集的”(即仅在当前页面附近稠密)。
代码如下:
PHP版本:
<? // Used by paginationHTML below... function paginationLink($p, $page, $URL) { if ($p==$page) return '<b style="color:#C0C0C0">' . $p . '</b>'; return '<a href="' . $URL . $p . '">' . $p . '</a>'; } // Used by paginationHTML below... function paginationGap($p1, $p2) { $x = $p2-$p1; if ($x==0) return ''; if ($x==1) return ' '; if ($x<=10) return ' . '; if ($x<=100) return ' .. '; return ' ... '; } // URL requires the $page number be appended to it. // eg it should end in '&page=' or something similar. function paginationHTML($page, $lastPage, $URL) { $LINKS_PER_STEP = 5; // Nav buttons if ($page>1) $result = '<form action="' . $URL . '1" method="POST" style="display:inline"><input type="submit" value=" |< "></form> ' . '<form action="' . $URL . ($page-1) . '" method="POST" style="display:inline"><input type="submit" value=" < "></form>'; else $result = '<input type="button" value=" |< " disabled> <input type="button" value=" < " disabled>'; $result .= ' ' . $page . ' '; if ($page<$lastPage) $result .= '<form action="' . $URL . ($page+1) . '" method="POST" style="display:inline"><input type="submit" value=" > "></form> ' . '<form action="' . $URL . $lastPage . '" method="POST" style="display:inline"><input type="submit" value=" >| "></form>'; else $result .= '<input type="button" value=" > " disabled> <input type="button" value=" >| " disabled>'; $result .= "<br>"; // Now calculate page links... $lastp1 = 1; $lastp2 = $page; $p1 = 1; $p2 = $page; $c1 = $LINKS_PER_STEP+1; $c2 = $LINKS_PER_STEP+1; $s1 = ''; $s2 = ''; $step = 1; while (true) { if ($c1>=$c2) { $s1 .= paginationGap($lastp1,$p1) . paginationLink($p1,$page,$URL); $lastp1 = $p1; $p1 += $step; $c1--; } else { $s2 = paginationLink($p2,$page,$URL) . paginationGap($p2,$lastp2) . $s2; $lastp2 = $p2; $p2 -= $step; $c2--; } if ($c2==0) { $step *= 10; $p1 += $step-1; // Round UP to nearest multiple of $step $p1 -= ($p1 % $step); $p2 -= ($p2 % $step); // Round DOWN to nearest multiple of $step $c1 = $LINKS_PER_STEP; $c2 = $LINKS_PER_STEP; } if ($p1>$p2) { $result .= $s1 . paginationGap($lastp1,$lastp2) . $s2; if (($lastp2>$page)||($page>=$lastPage)) return $result; $lastp1 = $page; $lastp2 = $lastPage; $p1 = $page+1; $p2 = $lastPage; $c1 = $LINKS_PER_STEP; $c2 = $LINKS_PER_STEP+1; $s1 = ''; $s2 = ''; $step = 1; } } } ?> <br><br><br> <?=paginationHTML(1,1,'?page=')?> <br><br><br> <?=paginationHTML(2,3,'?page=')?> <br><br><br> <?=paginationHTML(3,3,'?page=')?> <br><br><br> <?=paginationHTML(73,100,'?page=')?> <br><br><br> <?=paginationHTML(4,100,'?page=')?> <br><br><br> <?=paginationHTML(257,1000,'?page=')?> <br><br><br> <?=paginationHTML(7062,10555,'?page=')?> <br><br><br> <?=paginationHTML(22080,503456,'?page=')?>
ASP版本:
<% ' Used by paginationHTML below... Function paginationLink(p, page, URL) if p=page then paginationLink = "<b style=""color:#C0C0C0"">" & p & "</b>" else paginationLink = "<a href=""" & URL & p & """>" & p & "</a>" end if End Function ' Used by paginationHTML below... Function paginationGap(p1, p2) Dim x x = p2-p1 if x=0 then paginationGap = "" elseif x=1 then paginationGap = " " elseif x<=10 then paginationGap = " . " elseif x<=100 then paginationGap = " .. " else paginationGap = " ... " end if End Function ' URL requires the page number be appended to it. ' eg it should end in "&page=" or something similar. Function paginationHTML(page, lastPage, URL) const LINKS_PER_STEP = 5 Dim p1, p2, c1, c2, s1, s2, lastp1, lastp2, step ' Nav buttons if page>1 then paginationHTML = "<form action=""" & URL & "1"" method=""POST"" style=""display:inline""><input type=""submit"" value="" |< ""></form> " & _ "<form action=""" & URL & (page-1) & """ method=""POST"" style=""display:inline""><input type=""submit"" value="" < ""></form>" else paginationHTML = "<input type=""button"" value="" |< "" disabled> <input type=""button"" value="" < "" disabled>" end if paginationHTML = paginationHTML & " " & page & " " if page<lastPage then paginationHTML = paginationHTML & "<form action=""" & URL & (page+1) & """ method=""POST"" style=""display:inline""><input type=""submit"" value="" > ""></form> " & _ "<form action=""" & URL & lastPage & """ method=""POST"" style=""display:inline""><input type=""submit"" value="" >| ""></form>" else paginationHTML = paginationHTML & "<input type=""button"" value="" > "" disabled> <input type=""button"" value="" >| "" disabled>" end if paginationHTML = paginationHTML & "<br>" ' Now calculate page links... lastp1 = 1 lastp2 = page p1 = 1 p2 = page c1 = LINKS_PER_STEP+1 c2 = LINKS_PER_STEP+1 s1 = "" s2 = "" step = 1 do if c1>=c2 then s1 = s1 & paginationGap(lastp1, p1) & paginationLink(p1, page, URL) lastp1 = p1 p1 = p1+step c1 = c1-1 else s2 = paginationLink(p2, page, URL) & paginationGap(p2, lastp2) & s2 lastp2 = p2 p2 = p2-step c2 = c2-1 end if if c2=0 then step = step*10 p1 = p1+step-1 ' Round UP to nearest multiple of step p1 = p1-(p1 mod step) p2 = p2-(p2 mod step) ' Round DOWN to nearest multiple of step c1 = LINKS_PER_STEP c2 = LINKS_PER_STEP end if if p1>p2 then paginationHTML = paginationHTML & s1 & paginationGap(lastp1, lastp2) & s2 if (lastp2>page) or (page>=lastPage) then exit do lastp1 = page lastp2 = lastPage p1 = page+1 p2 = lastPage c1 = LINKS_PER_STEP c2 = LINKS_PER_STEP+1 s1 = "" s2 = "" step = 1 end if loop End Function %> <br><br><br> <%=paginationHTML(1,1,"?page=")%> <br><br><br> <%=paginationHTML(2,3,"?page=")%> <br><br><br> <%=paginationHTML(3,3,"?page=")%> <br><br><br> <%=paginationHTML(73,100,"?page=")%> <br><br><br> <%=paginationHTML(4,100,"?page=")%> <br><br><br> <%=paginationHTML(257,1000,"?page=")%> <br><br><br> <%=paginationHTML(7062,10555,"?page=")%> <br><br><br> <%=paginationHTML(22080,503456,"?page=")%>
Javascript版本(在完整的testing页面内):
<!doctype html> <html> <head> <title>Logarithmic Pagination Demo</title> <style> body {background:#C0C0C0;font-family:Arial,Helvetica,sans-serif;font-size:16px;text-align:left} div {margin:0;padding:0} div#setupDiv {margin:40px;text-align:center} table#datarows {border-collapse:collapse;margin:40px auto} table#datarows th {padding:5px 10px;background:#80B0FF;color:#FFFFFF;border:2px solid #80B0FF;width:1000px;text-align:center} table#datarows td {padding:2px 10px;background:#FFFFFF;color:#D0D0D0;border:2px solid #80B0FF;width:1000px;text-align:left;font-style:italic} input.err {border:2px solid #FF0000;background-color:#FFF0F0} form.pager {display:table;margin:0 auto;padding:20px;border:2px solid #E0E0E0;border-radius:10px;background-color:#D0D0D0;text-align:left;white-space:nowrap} form#pager1 {margin-top:40px} form#pager2 {margin-bottom:60px} form.pager div {display:table-cell;vertical-align:middle;padding:0 20px;white-space:nowrap} form.pager div + div {border-left:2px solid #E0E0E0} form.pager div.plinks {padding:0;border:0 none;font-size:14px;line-height:24px;max-width:800px;white-space:normal} form.pager div.plinks b {display:inline-block;vertical-align:bottom;font-size:24px;line-height:21px;height:24px;overflow:hidden;color:#808080} form.pager div.plinks a {text-decoration:none;color:black} form.pager div.plinks a:hover {color:#0000FF;font-weight:bold} form.pager div.plinks + div {border:0 none} </style> <script> var NumPages, RecsPerPage, els1, els2, plinks1, plinks2; function setupClick() { var el, n, r; el = document.getElementById("NumPages"); el.className = ((n = (el.value >>> 0)) ? "" : "err"); el = document.getElementById("RecsPerPage"); el.className = ((r = (el.value >>> 0)) ? "" : "err"); if (n&&r) { NumPages = n; RecsPerPage = r; setupServerPage(); } } // This function sets up what would normally be part of the server's HTML output. function setupServerPage() { var totRecs = NumPages * RecsPerPage, tbdy = document.getElementById("datarows").tBodies[0], l = tbdy.rows.length; document.getElementById("plength1").innerHTML = document.getElementById("plength2").innerHTML = totRecs + " record" + ((totRecs===1)?"":"s") + "<br>" + NumPages + " page" + ((NumPages===1)?"":"s"); els1["pcount"].value = els2["pcount"].value = NumPages; while (l>RecsPerPage) tbdy.deleteRow(--l); while (l<RecsPerPage) tbdy.insertRow(l++).insertCell(0).innerHTML = "Some data..."; pageNavigate(1); } // This would be handled by a return trip to the server, if not using AJAX. function pageClick(e) { e = e||window.event; var s = e.target||e.srcElement, n, p, el; if (s.tagName==="A") { n = (p = s.href).lastIndexOf("=")+1; pageNavigate(p.substring(n) >>> 0); return false; } else if ((s.tagName!=="INPUT")||(s.type!=="submit")) return; if (!(n = s.name)) { p = ((el = this.elements["p"]).value >>> 0); if ((p<=0)||(p>NumPages)) { el.className = "err"; return false; }} else if (n==="p1") p = 1; else if (n==="pprev") p = (this.elements["pcurr"].value >>> 0)-1; else if (n==="pnext") p = (this.elements["pcurr"].value >>> 0)+1; else if (n==="plast") p = (this.elements["pcount"].value >>> 0); pageNavigate(p); return false; } // This would also be handled by a return trip to the server, or else data records could be retrieved via AJAX. function pageNavigate(p) { els1["p"].className = els2["p"].className = els1["p"].value = els2["p"].value = ""; if (p<1) p = 1; else if (p>NumPages) p = NumPages; els1["p1"].disabled = els2["p1"].disabled = els1["pprev"].disabled = els2["pprev"].disabled = (p===1); els1["pnext"].disabled = els2["pnext"].disabled = els1["plast"].disabled = els2["plast"].disabled = (p===NumPages); els1["pcurr"].value = els2["pcurr"].value = p; // if the server is handling this, insert NON-logarithmic page links here (can be just first, current, and last page). plinks1.innerHTML = plinks2.innerHTML = logarithmicPaginationLinks(NumPages,p,"?p="); } // This function produces the logarithmic pagination links. function logarithmicPaginationLinks(lastPage,matchPage,linkURL) { function pageLink(p, page) { return ((p===page) ? "<b>"+p+"</b>" : '<a href="'+linkURL+p+'">'+p+"</a>"); } function pageGap(x) { if (x===0) return ""; if (x===1) return " "; if (x<=10) return " . "; if (x<=100) return " .. "; return " ... "; } var page = (matchPage ? matchPage : 1), LINKS_PER_STEP = 5, lastp1 = 1, lastp2 = page, p1 = 1, p2 = page, c1 = LINKS_PER_STEP+1, c2 = LINKS_PER_STEP+1, s1 = "", s2 = "", step = 1, linkHTML = ""; while (true) { if (c1>=c2) { s1 += pageGap(p1-lastp1) + pageLink(p1,matchPage); lastp1 = p1; p1 += step; c1--; } else { s2 = pageLink(p2,matchPage) + pageGap(lastp2-p2) + s2; lastp2 = p2; p2 -= step; c2--; } if (c2===0) { step *= 10; p1 += step-1; // Round UP to nearest multiple of step p1 -= (p1 % step); p2 -= (p2 % step); // Round DOWN to nearest multiple of step c1 = LINKS_PER_STEP; c2 = LINKS_PER_STEP; } if (p1>p2) { linkHTML += s1 + pageGap(lastp2-lastp1) + s2; if ((lastp2>page)||(page>=lastPage)) break; lastp1 = page; lastp2 = lastPage; p1 = page+1; p2 = lastPage; c1 = LINKS_PER_STEP; c2 = LINKS_PER_STEP+1; s1 = ''; s2 = ''; step = 1; } } return linkHTML; } window.onload = function() { els1 = document.getElementById("pager1").elements; els2 = document.getElementById("pager2").elements; plinks1 = document.getElementById("plinks1"); plinks2 = document.getElementById("plinks2") document.getElementById("pager1").onclick = document.getElementById("pager2").onclick = pageClick; (document.getElementById("setupDiv").lastChild.onclick = setupClick)(); } </script> </head> <body> <div id="setupDiv">Select number of pages: <input type="text" id="NumPages" value="100" size="7"> and records per page: <input type="text" id="RecsPerPage" value="20" size="7"> <input type="button" value=" Go "></div> <hr> <form id="pager1" class="pager" method="GET"><input type="hidden" name="pcount" value=""><input type="hidden" name="pcurr" value="1"> <div>Go to page: <input type="text" name="p" size="7"> <input type="submit" value=" Go "></div> <div><input type="submit" name="p1" value=" |< " disabled> <input type="submit" name="pprev" value=" < " disabled></div> <div id="plinks1" class="plinks"></div> <div><input type="submit" name="pnext" value=" > "> <input type="submit" name="plast" value=" >| "></div> <div id="plength1"></div> </form> <table id="datarows"><thead><tr><th>Column Heading...</th></tr></thead><tbody></tbody></table> <form id="pager2" class="pager" method="GET"><input type="hidden" name="pcount" value=""><input type="hidden" name="pcurr" value="1"> <div>Go to page: <input type="text" name="p" size="7"> <input type="submit" value=" Go "></div> <div><input type="submit" name="p1" value=" |< " disabled> <input type="submit" name="pprev" value=" < " disabled></div> <div id="plinks2" class="plinks"></div> <div><input type="submit" name="pnext" value=" > "> <input type="submit" name="plast" value=" >| "></div> <div id="plength2"></div> </form> </body> </html>
JavaScript中“对数页面导航”的简化版本:
生成下拉菜单。 这个例子只是document.write它,但你可以根据你的需要进一步开发(添加onChange等)。 Javascript转换感谢http://www.basereality.com/PHPToJavascript 。
<script> function paginationLink(p, page) { if (p == page) return '<option selected value="' + p + '">' + p + '</option>'; return '<option value="' + p + '">' + p+ '</option>'; } function paginationHTML(page, lastPage) { var LINKS_PER_STEP = 5; // Now calculate page links... var lastp1 = 1; var lastp2 = page; var p1 = 1; var p2 = page; var c1 = LINKS_PER_STEP + 1; var c2 = LINKS_PER_STEP + 1; var s1 = ''; var s2 = ''; var step = 1; var result = 0; while (true) { if (c1 >= c2) { s1 += paginationLink(p1, page); lastp1 = p1; p1 += step; c1--; } else { s2 = paginationLink(p2, page) + s2; lastp2 = p2; p2 -= step; c2--; } if (c2 == 0) { step *= 10; p1 += step - 1; // Round UP to nearest multiple of $step p1 -= (p1 % step); p2 -= (p2 % step); // Round DOWN to nearest multiple of $step c1 = LINKS_PER_STEP; c2 = LINKS_PER_STEP; } if (p1 > p2) { result += s1 + s2; if ((lastp2 > page) || (page >= lastPage)) return result; lastp1 = page; lastp2 = lastPage; p1 = page + 1; p2 = lastPage; c1 = LINKS_PER_STEP; c2 = LINKS_PER_STEP + 1; s1 = ''; s2 = ''; step = 1; } } } document.write('Menu generated with JavaScript <select>' + paginationHTML(765, 5055))+'</select>'; </script>
怎么样:
a)加上<-100 <-10 [分页] +10> +100>,而不是放弃分页本身
b)提供一个直接页面input[#..] [view],根据有效页面范围过滤input
c)需要一些适当的编码,但是:扩大内部的浮动范围,例如+/- 10,+/- 25,+/- 100页而不是吹掉整个寻呼范围
我想到了对数分页的两种select:
-
如果相关,您可以将数据分成几个部分,章节和书籍。 当纸是国王时,这是旧的方式,图书馆做了互联网的工作。 一些PDF文件仍然有。
-
如果有人想跳转到提到
supercalifragilisticexpialidocious
的大数据部分,您可以提供一个search框,ala Wikipedia。