循环遍历 jQuery 元素数组并在每个 'active' 元素出现在视图中时将其删除?



我已经创建了一个元素数组,我想在窗口滚动时不断循环,检查元素是否可见,如果可见,将其设置为活动,然后我想从数组中删除这个元素,最终检查数组是否为空,以解除滚动事件的绑定。

目前我很难知道应该用什么方式去除这个元素?我目前正在使用:

var index = innerItems.index($thisEl.index());
innerItems.splice(index, 1);

然而,这似乎把我用来检查视图中元素的函数搞砸了,我的数组长度似乎永远不会改变。

有人能推荐我如何实现我的目标,即在每个元素变为活动时删除它,直到我的数组为空并解除滚动事件的绑定吗?此外,如果有人能提供任何改进,那将是惊人的。

代码笔http://codepen.io/styler/pen/zDJrx

JS

var $mainContainer = $('.main-container'),
    innerItems = $mainContainer.children();
function isElementInViewport (el) {
    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }
    var rect = el.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}
function init() {
  itemChecker();
}
init();
$(window).on('scroll.windowScroll', itemChecker);
function itemChecker() {
  innerItems.each(function(i, element) {
    console.log('Index', i);
    console.log('Element', element);
    var $thisEl = $(element);
    // if isElementInViewport then add class is-active and remove from innerItems array
    var inView = isElementInViewport(element);
    if( inView ) {
      $thisEl.addClass('is-active');
      // Remove each element as it becomes ready/in view
      var index = innerItems.index($thisEl.index());
      innerItems.splice(index, 1);
    }
    console.log('innerItems length', innerItems.length);
    if( innerItems.length === 0 ) {
      $(window).off('scroll.windowScroll');
    }
  });
}

您的循环中有一个缺陷。你不能循环使用.each并移除循环中的项目。循环将期望初始项数,当涉及到被删除的索引时,它将抛出未定义的错误。

在这种情况下,应该使用基本上从数组末尾到开头的反向循环。或者正常循环,但在从数组中删除项的情况下更新索引变量。

反循环示例:

for (var i=arr.length;i--;) {
    if (i%2==0) {
         arr.splice(i, 1);
         // We removed the item but this will not 
         // interfere with our counting as we are doing it in reverse
    }
}

带索引更新的正常循环示例:

for (var i=0,len=arr.length;i<len;i++) {
    if (i%2==0) {
         arr.splice(i, 1);
         // We removed the item from the array and we need to decrease it's length by one
         len--;
    }
}

回到你的例子,这里有一个更新的版本,在这里用固定代码分叉代码笔。。

var $mainContainer = $('.main-container'),
    innerItemsCache = $mainContainer.children(),
    innerItemsVisible;
function isElementInViewport (el) {
    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }
    var rect = el.getBoundingClientRect();
    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}
function init() {
  itemChecker();
}
init();
$(window).on('scroll.windowScroll', itemChecker);
function itemChecker() {
  innerItemsVisible = [];
  for (var i=innerItemsCache.length; i--;) {
    console.log('Index', i);
    console.log('Element', element);
    var element = innerItemsCache[i];
    var $thisEl = $(element);
    // if isElementInViewport then add class is-active and remove from innerItems array
    var inView = isElementInViewport(element);
    if( inView ) {
      $thisEl.addClass('is-active');
      // Add elements that are visible
      innerItemsVisible.push(innerItemsCache[i]);
    } else {
      $thisEl.removeClass('is-active');
    }
    console.log('innerItems length', innerItemsVisible.length);
  }
   if( innerItemsVisible.length === 0 ) {
      $(window).off('scroll.windowScroll');
  }
}

正如您所看到的,我添加了一个缓存数组,这样您就不必在每次迭代中搜索所有项目。同时,由于您已经在遍历所有项目,因此更容易创建空数组并用可见项目填充它,如本工作示例中所示。。

如果可以使用jquery路点插件,您可以实现与相同的功能

$('.inner-container').waypoint(function () {
    $(this).addClass('is-active');
    if($(this).is(".inner-container:last-child")){
       alert("last item in view, destroying the functionality as you mentioned in comments");
       $(this).waypoint('destroy');
    }
}, {
    offset: 'bottom-in-view'
});
* {
    box-sizing:border-box
}
body {
    padding:0;
    margin:0
}
.main-container {
}
.inner-container {
    background: rgba(255, 0, 0, .4);
    width: 100%;
    height: 200px;
    border: 10px solid white;
}
.inner-container.is-active {
    background: rgba(255, 0, 0, .7);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/waypoints/2.0.3/waypoints.min.js"></script>
<div class="main-container">
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
    <div class="inner-container"></div>
</div>

最新更新