可滚动DIV;检查滚动到哪个 div 里面

Scrollable DIV; check which div inside is scrolled to

我正在研究日历。

结构很简单:外层div#calender包含所有带class.field和ID为#DD-MM-YYYY的日期字段。现在,当用户滚动经过 #01-02-2015 DIV 时,我希望月份名称位于顶部 ("January") 到二月,依此类推...所以月份名称是动态的。

问题: 如何检测滚动到哪个 div?

您无法真正知道哪个 div 滚动到 。您可以比较 div 的原始位置(相对于文档),并查看滚动的 window 是否已到达该位置。

window.pageYOffset 会在需要时为您提供文档已经滚动到的像素数。现在您需要的是将事件侦听器附加到 scroll 事件,给它相对于文档顶部的位置 #01-02-2015 div,并检查是否 window.pageYOffset 比那个大。如果是,您可以替换月份名称。如果少了,可以换回来。

要知道 #01-02-2015 div 相对于文档的偏移量,您可以 jquery 的 <a href="https://api.jquery.com/offset/" rel="nofollow noreferrer">offset()</a> 函数,或尝试以下方法,让我知道它是否有效:

function getOffsetTop(element){
    var offsetTop = 0;
    do {

      if ( !isNaN( element.offsetTop ) )
          offsetTop += element.offsetTop;
    } while( element = element.offsetParent );

    return offsetTop;
}

(改编自finding element's position relative to the document

** 编辑 **

因此,正如您所说,getOffsetTop 功能有效。在页面加载时,您需要获取所有 .first div 的 locations/offsets,假设您还给了它们 class 或 data-id 与具体月份。因此,让我们从创建这些值的数组开始,例如:

var firsts = document.querySelectorAll('.first');
var array = []
for(var i=0;i<firsts.length;i++)
    array.push({month:firsts[i].getAttribute('data-id'), offset:getOffsetTop(firsts[i])});

现在你有一个看起来像 [{month:'january',offset:214},{month:'february',offset:462}...].

的数组

同样,不看代码,我想你需要有一个全局变量(或者在滚动函数之外声明的)变量,在加载时声明,它存储正在查看的当前月份的索引(假设 window.pageYOffset 为零,您可以从数组 [0] 开始)。滚动函数将需要在本月之前和之后继续检查数组项,以查看是否达到了这些滚动点中的任何一个,如果是,请根据需要更改 div 内容,并更新 currentMonth变量。类似于:

var currentMonth = 0;
window.onscroll = function(){
    var index = currentMonth;
    if(array[index-1] && window.pageYOffset < array[index-1].offset)){
         // change the specific div's innerHTML to that of previous one. You can also target a particular `first` by using document.querySelector('.first[data-id="'+array[index].month+"]'):
         currentMonth = currentMonth - 1;
    }

    else if(array[index+1] && window.pageYOffset > array[index+1].offset)){
         // change the div to next one
         currentMonth = currentMonth + 1;
    }
}

我还没有测试过这些,但让我知道你是怎么弄出来的,或者如果控制台抛出错误。

** EDIT3 **:MDN 参考 (link) 建议使用 window.pageYOffset 而不是 window.scrollY 以实现跨浏览器兼容性,所以我更新了我的答案。

您可以使用 .offsetTop 检查每个单元格的当前 y 位置,计算当前显示在顶部的行,然后通过其 id 检查当前月份。

for (var i=0; i < rows.length; i++) {
    // Select the last day, incase the first few days are still from the previous month
    var lastcell = rows[i].querySelector(".field:last-of-type");

    // Check if offsetTop is smaller than 100 (leave some error margin)
    if (lastcell.offsetTop < 100) {
        var id = lastcell.attribute("id").split("-");
        var month = id[1];
        // Update month
    }
}

我会根据 .field 的高度和 #calendar 的当前滚动条来计算它。 当前卷轴div可用以下代码获取:

var position = document.getElementById('calendar').scrollTop;

添加隐藏的 div 作为每个月之间的分隔符(为此使用 css)。也许每个月都应该从新的 (css) 行开始。然后,您可以通过获取日历的滚动偏移量并查看分隔符之间的位置来检测您进入的月份。假设您知道每个分隔符的偏移量,或者您可以使用 jQuery 循环并获得那个更近了。

更新:

在每个月的日期之间添加一个隐藏的分隔符 div,例如 <span class="month-separator"></span>。那么根据定义,这些分隔符将具有不同的 offsets (因为整个月的日期都在它们之间)。然后当用户滚动时,通过每个分隔符计算当前 offset 循环并查看偏移量是否接近其中之一:

伪代码:

var calendarScroll = calendar.pageYOffset; // compute this
var whichMonth = 1; // start with january, i.e 1st month
// for each month separator
$('.month-separator').each(function(index){
   // break when encountering the month that is not yet scrolled to
   if ($(this).offset().top > calendarScroll) 
   {
      whichMonth = index;
      return false; // break from each loop
   }
});

您可能希望将上面的代码放入滚动处理程序中,例如 window.onscroll 事件处理程序(例如,参见 example with sticky header on scroll here)。

假设您已经准备好在滚动条上突出显示下一个日期的代码,您可能已经掌握了将突出显示的下一个日期的句柄。一个简单的方法是滚动,获取突出显示的 div 的值(应该是日期)。如果值 === 1,则为新月的第一天,因此显示下个月。

要获取下个月,您可以存储所有月份的数组,然后遍历该数组。如果当前迭代等于日历上的当前月份,return 数组的下一个元素

function getNextMonth(current_month) {
    var month_array = [
        'January',
        'February',
        'March',
        'etc'
    ];
    $.each(month_array, function(month, foo) {
        if(month == current_month) {
            return month_array[($.inArray(current_month, month_array) + 1) % month_array.length];
        }
    });
}

var calendar = $('calendar');

calendar.hover(function() {
    calendar.scroll(function() {
        //Your logic to change highlighted div
        var current_date = $('#datediv').val(),//However you are selecting your current date div
            month = $('#month_div');
        if(current_date === 1) {
            var next_month = getNextMonth(month);
            month.val(next_month);
        }
    });
});

老实说,从用户的角度来看,在这样的日历上滚动日期会很奇怪。对于我见过的大多数日历,滚动会改变月份。这对您来说可能是更好的设计方法,除非您必须按照现在的方式进行。