【问题标题】:JavaScript – detect if element stays in viewport for n secondsJavaScript – 检测元素是否在视口中停留 n 秒
【发布时间】:2018-08-01 22:51:55
【问题描述】:

每当某个 css 类的内容块可见 5 秒(表明用户正在阅读内容)时,我需要推送一个数据层事件。

我用过这样的东西:

$(window).on(‘scroll resize’, function() {
  $(‘.myClass’).each(function(element) {
    If (isInViewport(element)) {
      setTimeout(function() {
        if (isInViewport(element)) {
          ... // Push the data layer event.
        }
      }, 5000);
    }
  });
});

function isInViewport(element) {
  ... // Returns true if element is visible.
};

只是凭记忆写的,所以可能不是 100% 正确,但要点是我尝试:

  1. 在滚动/调整大小时测试每个 myClass 元素的可见性
  2. 如果有一个可见,请等待 5 秒,然后再检查一次相同的元素。

问题是,当 setTimeout 运行 isInViewport 时,元素未定义。也许 jQuery 的 .each 和 setTimeout 不匹配?

【问题讨论】:

  • 这实际上是一场糟糕的比赛。 scroll 在每个车轮旋转时都会像机关枪一样开火……对于这些事件中的每一个,.each() 循环开始遍历 X 个元素。然后... 5 秒后,element 也在范围内,它会是正确的吗?一定不! --- 所以你必须以一种不会在滚动或每个循环中定义的方式传递元素。我认为在滚动处理程序中使用数组是一个线索......但我现在不能说更多。
  • 第二次教导可能会有所帮助:我会将所有可见元素存储在一个数组中,当用户停止滚动时...然后,将超时设置为 5 秒以将新的可见元素与数组进行比较。两者之间的任何其他滚动都应清除超时......并等待滚动再次停止。 ;)
  • 你可以试试Intersection Observer。它处于实验状态,但除 IE 之外的所有浏览器都支持。

标签: javascript jquery settimeout viewport scrolltop


【解决方案1】:

我使用jquery-visible plugin 实现了一个脚本,该脚本将输出从特定元素可见的时间(以秒为单位)。输出使用 X 秒的间隔...在滚动处理程序之外。

在停止滚动时,我们会检查所有被监控的元素以了解它们是否在视口中。

如果一个元素是,我们检查它是否已经在上一次滚动停止时记录在visible_begins 数组中。如果不是,我们推送一个包含其id 和实际时间(以毫秒为单位)的对象。

仍处于滚动停止状态,如果某个元素不在视口中,我们检查它是否已记录在 visible_begins 中,如果是,我们将其删除。

现在每隔 X 秒(您的选择),我们检查所有被监控的元素,并且仍然在视口中的每个元素都与现在的时间差一起输出。

console.clear();

var scrolling = false;
var scrolling_timeout;
var reading_check_interval;
var reading_check_delay = 5;    // seconds
var completePartial = false;     // "true" to include partially in viewport
var monitored_elements = $(".target");
var visible_begins = [];


// Scroll handler
$(window).on("scroll",function(){
  if(!scrolling){
    console.log("User started scrolling.");
  }
  scrolling = true;

  clearTimeout(scrolling_timeout);
  scrolling_timeout = setTimeout(function(){
    scrolling = false;
    console.log("User stopped scrolling.");

    // User stopped scrolling, check all element for visibility
    monitored_elements.each(function(){
      if($(this).visible(completePartial)){
        console.log(this.id+" is in view.");

        // Check if it's already logged in the visible_begins array
        var found = false;
        for(i=0;i<visible_begins.length;i++){
          if(visible_begins[i].id == this.id){
            found = true;
          }
        }
        if(!found){
          // Push an object with the visible element id and the actual time
          visible_begins.push({id:this.id,time:new Date().getTime()});
        }
      }
    });
  },200);   // scrolling delay, 200ms is good.
}); // End on scroll handler


// visibility check interval
reading_check_interval = setInterval(function(){
  monitored_elements.each(function(){
    if($(this).visible(completePartial)){
      // The element is visible
      // Check all object in the array to fing this.id
      for(i=0;i<visible_begins.length;i++){
        if(visible_begins[i].id == this.id){
          var now = new Date().getTime();
          var readTime = ((now-visible_begins[i].time)/1000).toFixed(1);
          console.log(visible_begins[i].id+" is in view since "+readTime+" seconds.")
        }

      }
    }else{
      // The element is not visible
      // Remove it from thevisible_begins array if it's there
      for(i=0;i<visible_begins.length;i++){
        if(visible_begins[i].id == this.id){
          visible_begins.splice(i,1);
          console.log(this.id+" was removed from the array.");
        } 
      }
    }
  });
},reading_check_delay*1000);  // End interval
.target{
  height:400px;
  border-bottom:2px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-visible/1.2.0/jquery.visible.min.js"></script>

<div id="one" class="target">1</div>
<div id="two" class="target">2</div>
<div id="three" class="target">3</div>
<div id="four" class="target">4</div>
<div id="five" class="target">5</div>
<div id="six" class="target">6</div>
<div id="seven" class="target">7</div>
<div id="eight" class="target">8</div>
<div id="nine" class="target">9</div>
<div id="ten" class="target">10</div>

请在全页模式下运行 sn-p,因为有几个控制台日志。

CodePen

【讨论】:

  • 这就是我无法入睡时发生的事情。 ;)
  • 这是一个更好的原则。谢谢。
【解决方案2】:

您可以使用此功能检查元素是否在视口中(来自this 答案):

function isElementInViewport (el) {

    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() */
    );
}

<input id="inViewport"/>
<span style="margin-left: 9999px;" id="notInViewport">s</span>
<script>
function isElementInViewport (el) {

    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() */
    );
}
console.log("#inViewport in viewport: "+isElementInViewport(document.getElementById("inViewport")));
console.log("#notInViewport in viewport: "+isElementInViewport(document.getElementById("notInViewport")));
</script>

【讨论】:

    【解决方案3】:

    您可以尝试使用Waypoints,它是一个允许您确定元素何时进入或离开该视口的库。您向它传递一个接受方向参数的事件处理程序。方向告诉您被跟踪的元素是进入还是退出屏幕。一旦您检测到元素已进入屏幕,然后启动计时器。如果您没有看到元素退出视口的时间和事件,那么您知道它已经在屏幕上显示了那段时间。

    【讨论】:

      【解决方案4】:

      我设法使用交叉点观察器做到了这一点。我的要求是检查元素是否在 50% 的视野中至少一秒钟,如果是,则触发一个事件。

      let timer;
      
      const config = {
          root: null,
          threshold: 0.5 // This was the element being 50% in view (my requirements)
      };
      
      const observer = new IntersectionObserver((entries) => {
          entries.forEach((entry) => {
      
              if (entry.isIntersecting) {
                  timer = setTimeout(() => {
                     //... push to data layer
                  }, 1000);
              } else {
                  clearTimeout(timer);
              }
          });
      }, config);
      
      observer.observe(YourElement);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-02-12
        • 1970-01-01
        • 2015-09-05
        • 2020-07-14
        • 2020-07-05
        • 2018-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多