跨浏览器的Javascript垂直滚动百分比确定方法

77

我如何在任意时刻查找用户通过垂直滚动条移动了多少比例?

捕获 onscroll 事件以响应用户向下滚动页面很容易,但在事件内部如何确定用户已经滚动了多远呢?在这种情况下,特别重要的是百分比。我并不担心 IE6 的解决方案。

主要框架(Dojo、jQuery、Prototype、Mootools)中是否有任何一种方式可以简单地跨浏览器提供此功能?

14个回答

145

2016年10月:已修复。在jsbin演示中,括号在答案中丢失。糟糕。

适用于Chrome、Firefox和IE9+。在jsbin上查看实时演示

var h = document.documentElement, 
    b = document.body,
    st = 'scrollTop',
    sh = 'scrollHeight';

var percent = (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;

作为函数:

function getScrollPercent() {
    var h = document.documentElement, 
        b = document.body,
        st = 'scrollTop',
        sh = 'scrollHeight';
    return (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100;
}

如果您喜欢使用 jQuery(原始答案):

$(window).on('scroll', function(){
  var s = $(window).scrollTop(),
      d = $(document).height(),
      c = $(window).height();

  var scrollPercent = (s / (d - c)) * 100;
  
  console.clear();
  console.log(scrollPercent);
})
html{ height:100%; }
body{ height:300%; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


1
我已经添加了一个最佳的IE9+版本,并提供了实时演示,同时也恢复了原始的jQuery解决方案。 - Phil Ricketts
1
对我来说,在Chrome上它有点可行,但是在最新的Firefox上却不行。它会给出NaN。我指的是原生JavaScript。有什么想法吗?@PhilRicketts - bozzmob
1
当我尝试这个时,我得到了“无穷大”。发现document.documentElement.scrollHeightdocument.documentElement.clientHeight相等,导致除以零。我正在使用Chromium版本72.0.3626.121(官方版本),构建于Ubuntu。 - Spikatrix
3
通过添加这段 CSS 代码 html { height: 100%; },解决了Infinity issue - Spikatrix
14
为什么你要使用如此短的变量名?这会让代码难以阅读。对我来说看起来像是压缩后的JS。 - Rudey
显示剩余11条评论

33

我认为我找到了一个不依赖于任何库的好解决方案:

/**
 * Get current browser viewpane heigtht
 */
function _get_window_height() {
    return window.innerHeight || 
           document.documentElement.clientHeight ||
           document.body.clientHeight || 0;
}

/**
 * Get current absolute window scroll position
 */
function _get_window_Yscroll() {
    return window.pageYOffset || 
           document.body.scrollTop ||
           document.documentElement.scrollTop || 0;
}

/**
 * Get current absolute document height
 */
function _get_doc_height() {
    return Math.max(
        document.body.scrollHeight || 0, 
        document.documentElement.scrollHeight || 0,
        document.body.offsetHeight || 0, 
        document.documentElement.offsetHeight || 0,
        document.body.clientHeight || 0, 
        document.documentElement.clientHeight || 0
    );
}


/**
 * Get current vertical scroll percentage
 */
function _get_scroll_percentage() {
    return (
        (_get_window_Yscroll() + _get_window_height()) / _get_doc_height()
    ) * 100;
}

纯JavaScript的完美答案..! - Prasath K
我尝试了其他解决方案,但只有这个对我完美地起作用了。谢谢。 - Keval
这个解决方案不能给出0%-100%的结果,因为它在开始时添加了视口高度,要使其变为0-100,您应该从文档总高度中减去高度:_get_window_Yscroll() / (_get_doc_height() - _get_window_height())。 - Caio Vertematti
1
@CaioVertematti,这就是我们的目标。它显示了用户已查看的整个页面的百分比。如果用户没有滚动但整个页面适合视口,它会显示100。理论上,如果页面非常长和/或视口非常短,它可以给你0。 - Eduardo

16

这应该就可以解决问题,不需要使用任何库:

function currentScrollPercentage()
{
    return ((document.documentElement.scrollTop + document.body.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight) * 100);
}

这已经很接近了,谢谢。然而,在至少Chrome浏览器中,document.documentElement.scrollHeight和document.documentElement.clientHeight两者相等(内容的高度),因此分母始终为零。 - majelbstoat
3
嗯,我刚测试过,对于Chrome、IE和Firefox来说都能正常工作。您是否正在使用官方最新版本的Chrome浏览器? - Mark Bell
1
Chrome 版本为 5.0.335.1 beta。问题肯定是分母计算结果为零,因为该函数(完全复制粘贴)始终返回无穷大 :/ - majelbstoat
感谢您对此提供的指引。如果您对这个问题的用途感兴趣,可以查看这个链接:http://jamietalbot.com/projects/css/morning-sunset/ 。有时候,看到潜在的学术问题和答案在现实世界中产生的效果是很不错的...(但如果您认为这是不恰当的自我推销,请随意删除此评论)。 - majelbstoat
这个简化版本对我在 Google Chrome 上也起作用: document.body.scrollTop * 100 / (document.documentElement.scrollHeight - document.documentElement.clientHeight) - cprcrack
显示剩余5条评论

11

这些方法在我使用Chrome 19.0、FF12和IE9时完美运行:

function getElementScrollScale(domElement){
        return domElement.scrollTop / (domElement.scrollHeight - domElement.clientHeight);
    }

function setElementScrollScale(domElement,scale){
        domElement.scrollTop = (domElement.scrollHeight - domElement.clientHeight) * scale;
    }

谢谢@toske,我找到了几个复杂的答案,但都不起作用,直到我找到了你的答案。正是我想要的。 - Rocky Sims

6
每个人都有很好的答案,但我只需要一个变量的答案。我不需要一个事件监听器,我只想获取滚动的百分比。这就是我得到的:
const scrolledPercentage = 
    window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)

document.addEventListener("scroll", function() {
  const height = window.scrollY / (document.documentElement.scrollHeight - document.documentElement.clientHeight)

  document.getElementById("height").innerHTML = `Height: ${height}`
})
.container {
  position: relative;
  height: 200vh;
}

.sticky-div {
  position: sticky;
  top: 0;
}
<!DOCType>
<html>

<head>
</head>

<body>
  <div id="container" class="container">
    <div id="height" class="sticky-div">
      Height: 0
    </div>
  </div>
</body>


3
一个TypeScript实现。
function getScrollPercent(event: Event): number {
  const {target} = event;
  const {documentElement, body} = target as Document;
  const {scrollTop: documentElementScrollTop, scrollHeight: documentElementScrollHeight, clientHeight} = documentElement;
  const {scrollTop: bodyScrollTop, scrollHeight: bodyScrollHeight} = body;
  const percent = (documentElementScrollTop || bodyScrollTop) / ((documentElementScrollHeight || bodyScrollHeight) - clientHeight) * 100;
  return Math.ceil(percent);
}

捕获到类型错误:无法解构属性'documentElement'的'scrollTop',因为它未定义。 - lhk

2
这个问题已经存在很长时间了,我知道,但是在尝试解决同样的问题时,我偶然发现了它。以下是我用jQuery解决它的方法:
首先,我将想要滚动的内容包装在一个div中(不太语义化,但有帮助)。然后在包装器上设置overflow和height属性。
<div class="content-wrapper" style="overflow: scroll; height:100px">
    <div class="content">Lot of content that scrolls</div>
</div>

最终,我能够根据这些指标计算出滚动条的百分比:
var $w = $(this),
    scroll_top = $w.scrollTop(),
    total_height = $w.find(".content").height(),        
    viewable_area = $w.height(),
    scroll_percent = Math.floor((scroll_top + viewable_area) / total_height * 100);                

这里有一个带有工作示例的fiddle:http://jsfiddle.net/prEGf/

2
如果您正在使用Dojo,您可以执行以下操作:
var vp = dijit.getViewport();
return (vp.t / (document.documentElement.scrollHeight - vp.h));

这将返回0到1之间的值。


3
因为在同一天回答的其他人没有给出完整的答案,而且每个其他的答案都是在我标记为正确之后一年以上才得到的 :) - majelbstoat
如果您认为其他答案更好,那么移动勾选也没有问题。"您可以随时更改接受的答案,或者取消接受答案" 好的问题维护是很重要的。;^) - ruffin

1

首先,将事件监听器附加到您想要跟踪的某个文档

yourDocument.addEventListener("scroll", documentEventListener, false);

然后:

function documentEventListener(){
  var currentDocument  = this;
  var docsWindow       = $(currentDocument.defaultView); // This is the window holding the document
  var docsWindowHeight = docsWindow.height(); // The viewport of the wrapper window
  var scrollTop        = $(currentDocument).scrollTop(); // How much we scrolled already, in the viewport
  var docHeight        = $(currentDocument).height();    // This is the full document height.

  var howMuchMoreWeCanScrollDown = docHeight - (docsWindowHeight + scrollTop);
  var percentViewed = 100.0 * (1 - howMuchMoreWeCanScrollDown / docHeight);
  console.log("More to scroll: "+howMuchMoreWeCanScrollDown+"pixels. Percent Viewed: "+percentViewed+"%");
}

0

我已经审查了上面所有的内容,但它们使用了更复杂的方法来解决问题。我通过一个简单的数学公式找到了答案。

这个公式是:Value/Total * 100。比如说,如果Total是200,你想知道100占200的百分比,那么你可以这样计算:100/200 * 100% = 50%(即为所求的Value)。

pageYOffset表示垂直滚动的距离,不包括边框。当你滚动到底部时,它的值达到最大值。

offsetHeight表示页面的总高度,包括边框! clientHeight表示页面的高度,不包括边框,但也不包括内容的末尾!

当你滚动到底部时,例如,你得到了pageyoffset的值为1000,而offsetHeight的值为1200,clientHeight的值为200。那么,1200-200(clientheight)= 1000(pageYOffset)的值在offsetHeight中,因此scrollPosition300(1000的300)/1000 * 100 = 30%。

`pageOffset = window.pageYOffset; pageHeight = document.documentElement.offsetHeight; clientHeight = document.documentElement.clientHeight;

percentage = pageOffset / (pageHeight - clientHeight) * 100 + "%";
console.log(percentage)`

我们必须执行offsetHeight - clientHeight的原因是,clientHeight显示所有可用内容(不包括边框)的像素高度,而offsetHeight显示包括边框在内的可用内容的高度。pageYOffset计算滚动次数;滚动条很长,需要计算整个窗口,它计算滚动本身直到达到末尾,在滚动条中可用的空间以px为单位的pageYOffset,因此要达到该数字,您需要减去offsetHeight - clientHeight以将其降至pageYOffset的较低值。
我上电脑后会更新,请留言让我清楚地知道,这样我就不会忘记了!谢谢 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接