javascript 滚动
在浏览较长的文档时,当用户单击跳到同一文档中另一个位置的链接时,常常会感到困惑或迷失方向。
它们是在同一页面上还是在不同页面上? 他们应该从这里滚动更多吗? 这是怎么回事?
解决此问题的方法是像这样将用户滚动到整个文档中的链接位置。 在本教程中,我们将使用少量JavaScript来确保文档内部的链接将用户滚动到目的地,而不是直接跳转到目的地并使用户感到困惑。
查找内部链接
首先,我们需要确定文档中的所有链接,然后确定其中哪些是内部链接。 获取所有链接的列表很容易:
var allLinks = document.getElementsByTagName('a');
我们需要遍历此列表,并找出我们发现的哪些链接是内部的。 内部链接中将带有一个井号(#),它指向我们当前正在查看的文档。 有用的location对象告诉我们我们正在查看的文档的URL,因此请尝试以下操作:
for (var i=0;i<allLinks.length;i++) {
var lnk = allLinks[i];
if ((lnk.href && lnk.href.indexOf('#') != -1) &&
( (lnk.pathname == location.pathname) ||
('/'+lnk.pathname == location.pathname) ) &&
(lnk.search == location.search)) {
DO SOMETHING WITH THE LINK HERE
}
}
在这里,for循环遍历文档中的链接列表,然后检查三件事:
- 链接是否包含哈希?
我们使用链接的href属性和indexOf()
函数进行检查,以查找一个字符串在另一个字符串中的位置。 - 链接与当前位置相同吗?
链接(和位置对象)具有pathname属性。 在某些浏览器中,URL http://www.sitepoint.com/about/who/mharbottle.php的路径名是/about/who/mharbottle.php,在其他浏览器中则是about / who / mharbottle.php(请注意存在或没有第一个斜杠)。 我们必须检查两者。 - 查询字符串是否与当前位置相同?
querystring是出现在?之后的所有内容。 在网址中; 如果您的站点是数据库驱动的,那么这显然很重要。 JavaScript在位置和包含查询字符串的链接上定义了搜索属性。
如果每个问题都是正确的,那么我们知道链接是内部的,可以将其设置为滚动到其目的地。
滚动,不要跳!
现在,我们确定了一个内部链接,我们希望在单击它时使其滚动。 为此,我们需要在链接上附加一个onclick事件处理程序。 在过去的日子里,当Web开发人员大胆使用时,许多人(我确实)认为事件处理程序是在HTML内的链接上设置的:
<a href="http://www.sitepoint.com/" onclick="myEventHandler()">
但这不是真的。 相反,您应该将事件侦听器附加到链接对象。 W3C指定了一种标准方法来执行此操作,Internet Explorer也是如此。 Scott Andrew提供了有用的函数来处理这两种情况:
function ss_addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+, NS6 and Mozilla
// By Scott Andrew
{
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent){
var r = elm.attachEvent("on"+evType, fn);
return r;
}
}
因此,在链接的循环中,我们调用此脚本将平滑滚动功能附加到内部链接:
ss_addEvent(lnk,'click',smoothScroll);
如何滚动
当然,我们实际上也必须有一个smoothScroll()
函数。 这是一个复杂的方面,因为这全都与查找对象在页面上的位置有关,并且不同的浏览器以各种方式实现这一点。 出色的安德鲁·克洛弗(Andrew Clover)撰写了如何在浏览器中找到该位置的摘要,我们将在此处广泛使用此解决方案。
首先,我们的smoothScroll
函数是一个事件处理程序,因此,在调用它时(即,当用户单击我们的内部链接之一时),我们需要检索被单击的链接。 Netscape类浏览器将事件对象传递给每个处理程序。 Internet Explorer将这些详细信息存储在全局window.event
对象中。
if (window.event) {
target = window.event.srcElement;
} else if (e) {
target = e.target;
} else return;
此代码以跨浏览器的方式将单击的链接设置为目标。 …差不多 Mozilla有时会在链接中将文本节点作为单击项传递给您。 我们需要检查目标是否为文本节点(即其nodeType
是否为3),如果是,则采用其父节点。
if (target.nodeType == 3) { target = target.parentNode; }
只是偏执,我们还检查了我们是否有一个A标签,以防我们错过了一些东西:
if (target.nodeName.toLowerCase() != 'a') return;
现在,我们需要找到目标: <a name>
标记,它对应于我们单击的链接中哈希之后的部分。 链接的哈希属性包含#和URL中出现在其后的部分,因此,让我们遍历文档中的所有链接,并检查其name属性是否等于所单击链接的哈希部分:
// First strip off the hash (first character)
anchor = target.hash.substr(1);
// Now loop all A tags until we find one with that name
var allLinks = document.getElementsByTagName('a');
var destinationLink = null;
for (var i=0;i<allLinks.length;i++) {
var lnk = allLinks[i];
if (lnk.name && (lnk.name == anchor)) {
destinationLink = lnk;
break;
}
}
// If we didn't find a destination, give up and let the browser do
// its thing
if (!destinationLink) return true;
我们知道单击的内容以及指向的内容。 现在,我们需要知道的是我们在文档中的位置以及目的地。 这是Andy Clover的笔记不可估量的地方。 首先,我们找到目标链接的位置:
var destx = destinationLink.offsetLeft;
var desty = destinationLink.offsetTop;
var thisNode = destinationLink;
while (thisNode.offsetParent &&
(thisNode.offsetParent != document.body)) {
thisNode = thisNode.offsetParent;
destx += thisNode.offsetLeft;
desty += thisNode.offsetTop;
}
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
请注意,按照IE的要求,我们循环遍历offsetParents
直到到达文档正文。 接下来,计算出我们当前的位置:
function ss_getCurrentYPos() {
if (document.body && document.body.scrollTop)
return document.body.scrollTop;
if (document.documentElement && document.documentElement.scrollTop)
return document.documentElement.scrollTop;
if (window.pageYOffset)
return window.pageYOffset;
return 0;
}
IE5和5.5将当前位置存储在document.body.scrollTop
,IE6存储在document.documentElement.scrollTop
,而Netscape类浏览器存储在window.pageYOffset
。 !
我们实际处理滚动的方式是使用setInterval()
; 此功能非常有用,它设置了一个重复计时器,可以触发我们选择的功能。 在这种情况下,我们将让我们的功能将浏览器的位置移至离目的地更近的一步; setInterval()
将反复调用我们的函数,当到达目的地时,我们将取消计时器。
首先,使用clearInterval()
关闭当前正在运行的所有计时器:
clearInterval(ss_INTERVAL);
ss_INTERVAL
is a global variable in which we will later store the ouput ofsetInterval()
. Next, work out how big each step should be:ss_stepsize = parseInt((desty-cypos)/ss_STEPS);
ss_STEPS
is defined in the script to be the number of steps we take from target to destination. Our "scroll one step" function is calledss_scrollWindow
and takes three parameters:
- 滚动多少
- 目的地位置
- 目标链接本身
我们需要在一个字符串中构造对此调用,并将该字符串以及我们希望重复调用的频率传递给setInterval
:
ss_INTERVAL = setInterval('ss_scrollWindow('+ss_stepsize+','+desty+',"'+anchor+'")',10);
请注意,我们是如何构建一个对ss_scrollWindow()
的调用的字符串,而不是直接调用ss_scrollWindow()
的字符串-这是setInterval()
最令人困惑的事情之一。
完成此操作后,我们必须通过遵循链接并直接跳至目标位置来停止浏览器的正常运行。 同样,这在不同的浏览器中会发生不同的情况。 要停止浏览器在Internet Explorer中正常处理此事件,请使用:
if (window.event) {
window.event.cancelBubble = true;
window.event.returnValue = false;
}
请注意window.event
的检查,以确保我们使用的是IE。
要在Netscape类浏览器中执行相同的操作,请使用以下代码:
if (e && e.preventDefault && e.stopPropagation) {
e.preventDefault();
e.stopPropagation();
}
滚动步骤
最后一件事:我们实际上如何进行滚动? 这里的关键功能是window.scrollTo()
,您将X和Y位置传递给该window.scrollTo()
; 然后浏览器将窗口滚动到该位置。 一个小皱纹是您无法一直滚动到底部。 如果您传递的Y位置距离文档底部的窗口高度小于,则浏览器将仅向下滚动至最远距离-显然,如果与页面底部小于窗口的高度。
现在,我们需要检查一下; 最好的方法是查看滚动前后的位置是否相同:
function ss_scrollWindow(scramount,dest,anchor) {
wascypos = ss_getCurrentYPos();
isAbove = (wascypos < dest);
window.scrollTo(0,wascypos + scramount);
iscypos = ss_getCurrentYPos();
isAboveNow = (iscypos < dest);
if ((isAbove != isAboveNow) || (wascypos == iscypos)) {
// if we've just scrolled past the destination, or
// we haven't moved from the last scroll (i.e., we're at the
// bottom of the page) then scroll exactly to the link
window.scrollTo(0,dest);
// cancel the repeating timer
clearInterval(ss_INTERVAL);
// and jump to the link directly so the URL's right
location.hash = anchor;
}
}
请注意,由于我们以特定的整数增量滚动,因此此步骤可能使我们越过了目的地。 因此,我们检查滚动前后是否在链接上方; 如果这两个位置不同,那么我们将滚动到链接上方,这样就完成了。 完成后,我们取消计时器并设置页面的URL(通过设置位置对象的一部分),以使浏览器看起来好像在处理链接。
产生效果
将这种效果应用于页面的最简单方法是将代码拖放到名为smoothscroll.js的文件中,并使用以下代码将该文件包含在页面中:
<script src="smoothscroll.js" type="text/javascript"></script>
这种方法遵循不打扰的DHTML原理,使每个人都易于使用。 为了使解决方案起作用,脚本需要以某种方式运行; 我们将第一步的代码(遍历链接以查找内部链接)放入函数ss_fixAllLinks()
,然后使用Scott Andrew的函数将其绑定到窗口的onload事件:
ss_addEvent(window,"load",ss_fixAllLinks);
整个代码如下所示:
function ss_fixAllLinks() {
// Get a list of all links in the page
var allLinks = document.getElementsByTagName('a');
// Walk through the list
for (var i=0;i<allLinks.length;i++) {
var lnk = allLinks[i];
if ((lnk.href && lnk.href.indexOf('#') != -1) &&
( (lnk.pathname == location.pathname) ||
('/'+lnk.pathname == location.pathname) ) &&
(lnk.search == location.search)) {
// If the link is internal to the page (begins in #)
// then attach the smoothScroll function as an onclick
// event handler
ss_addEvent(lnk,'click',smoothScroll);
}
}
}
function smoothScroll(e) {
// This is an event handler; get the clicked on element,
// in a cross-browser fashion
if (window.event) {
target = window.event.srcElement;
} else if (e) {
target = e.target;
} else return;
// Make sure that the target is an element, not a text node
// within an element
if (target.nodeType == 3) {
target = target.parentNode;
}
// Paranoia; check this is an A tag
if (target.nodeName.toLowerCase() != 'a') return;
// Find the <a name> tag corresponding to this href
// First strip off the hash (first character)
anchor = target.hash.substr(1);
// Now loop all A tags until we find one with that name
var allLinks = document.getElementsByTagName('a');
var destinationLink = null;
for (var i=0;i<allLinks.length;i++) {
var lnk = allLinks[i];
if (lnk.name && (lnk.name == anchor)) {
destinationLink = lnk;
break;
}
}
// If we didn't find a destination, give up and let the browser do
// its thing
if (!destinationLink) return true;
// Find the destination's position
var destx = destinationLink.offsetLeft;
var desty = destinationLink.offsetTop;
var thisNode = destinationLink;
while (thisNode.offsetParent &&
(thisNode.offsetParent != document.body)) {
thisNode = thisNode.offsetParent;
destx += thisNode.offsetLeft;
desty += thisNode.offsetTop;
}
// Stop any current scrolling
clearInterval(ss_INTERVAL);
cypos = ss_getCurrentYPos();
ss_stepsize = parseInt((desty-cypos)/ss_STEPS);
ss_INTERVAL = setInterval('ss_scrollWindow('+ss_stepsize+','+desty+',"'+anchor+'")',10);
// And stop the actual click happening
if (window.event) {
window.event.cancelBubble = true;
window.event.returnValue = false;
}
if (e && e.preventDefault && e.stopPropagation) {
e.preventDefault();
e.stopPropagation();
}
}
function ss_scrollWindow(scramount,dest,anchor) {
wascypos = ss_getCurrentYPos();
isAbove = (wascypos < dest);
window.scrollTo(0,wascypos + scramount);
iscypos = ss_getCurrentYPos();
isAboveNow = (iscypos < dest);
if ((isAbove != isAboveNow) || (wascypos == iscypos)) {
// if we've just scrolled past the destination, or
// we haven't moved from the last scroll (i.e., we're at the
// bottom of the page) then scroll exactly to the link
window.scrollTo(0,dest);
// cancel the repeating timer
clearInterval(ss_INTERVAL);
// and jump to the link directly so the URL's right
location.hash = anchor;
}
}
function ss_getCurrentYPos() {
if (document.body && document.body.scrollTop)
return document.body.scrollTop;
if (document.documentElement && document.documentElement.scrollTop)
return document.documentElement.scrollTop;
if (window.pageYOffset)
return window.pageYOffset;
return 0;
}
function ss_addEvent(elm, evType, fn, useCapture)
// addEvent and removeEvent
// cross-browser event handling for IE5+, NS6 and Mozilla
// By Scott Andrew
{
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent){
var r = elm.attachEvent("on"+evType, fn);
return r;
}
}
var ss_INTERVAL;
var ss_STEPS = 25;
ss_addEvent(window,"load",ss_fixAllLinks);
结语
您的文档内部链接将滚动到其目的地,使您的用户可以了解浏览器在文档中的位置以及距起点的距离。 该代码已经过测试,可以在Mozilla,IE和Opera中运行; 它在Konqueror中不起作用,并且假定在其他浏览器中不起作用。
javascript 滚动