(最初这是作为操作提示发布的,我的答案包含在问题中。我现在将我的答案分成下面的“答案”部分)。
更具体地说:
假设您要向用户显示一组记录,这些记录分为固定大小的页面(例如 Google 搜索的结果)。 如果只有几个页面,您可以在结果末尾显示一个页面导航区域,可能如下所示:
[ << ] [<] 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 [ > ] [ >> ]
但在这两种情况下,导航到“...”部分中间的任何位置都需要多次单击鼠标。 有时会提供直接输入页码的输入框;否则(假设我们在这里讨论的是网页)精明的用户可能会查看 URL 以查看是否可以直接编辑它。
如果有一个分页显示,让用户只需点击几下鼠标即可到达任何页面,而不需要有太多可笑的链接,那就太好了。
如何最好地实现这一目标?
这可以通过根据距端点或当前页面的距离以对数方式分布页码来实现。 这是我的意思的一个例子:
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
注意间隙中的编号如何从 1 变为 10、100(等等)。 (我使用 10 的幂,但原则上你可以使用不同的方案 - 比如说 2 的幂)。
我在 2004 年编写了一些执行此操作的代码,并想在这里分享它。 有 PHP 和 ASP 版本,但逻辑应该很简单,可以翻译成任何语言。 请注意,底部的位(在两种情况下)仅显示一些示例。 显然,格式需要自定义才能匹配您的网页(或应用程序),因此这里的格式非常基本。 更改
LINKS_PER_STEP
中的 paginationHTML
以确定当您远离端点或当前页面时,步长增加之前显示的数字数量。
为了获得更紧凑的输出,您还可以考虑更改代码,以使端点周围的编号不会“密集”(即仅在当前页面周围密集)。
这是代码:
<?
// 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.
// e.g. 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=')?>
<%
' 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.
' e.g. 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=")%>
<!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>
生成下拉菜单。这个示例只是 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 [pagination] +10> +100> 而不是放大分页本身
b) 提供直接页面输入 [# .. ] [ view ] ,根据有效页面范围过滤输入
c) 需要一些适当的编码,但是:通过 +/-10、+/-25、+/-100 页扩展内部浮动范围,而不是放大整个分页范围
我想到了两种对数分页的替代方案:
如果相关,您可以将数据分成部分、章节和书籍。这是古老的方式,当时纸张为王,图书馆取代了互联网的作用。有些PDF文档仍然有这个。
如果有人想跳转到提到
supercalifragilisticexpialidocious
的大数据部分,您可以提供一个搜索框,如维基百科。