JavaScript – 检测元素是否在视口中停留 n 秒
JavaScript – detect if element stays in viewport for n seconds
每当某个 css class 的内容块可见 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% 正确,但要点是我尝试:
- 测试 scroll/resize
上每个 myClass 元素的可见性
- 如果有一个可见,请等待 5 秒并再次检查同一元素。
问题是,当 setTimeout 运行 isInViewport 时,元素未定义。也许 jQuery 的 .each 和 setTimeout 不匹配?
您可以尝试使用 Waypoints,它是一个库,可让您确定元素何时进入或离开视口。您向它传递一个接受方向参数的事件处理程序。方向告诉您被跟踪的元素是进入还是退出屏幕。一旦您检测到元素已进入屏幕,然后启动计时器。如果您没有看到元素退出视口的时间和事件,那么您就知道它已经在屏幕上显示了那段时间。
您可以使用此函数检查元素是否在视口中(来自 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>
我使用 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>
请运行全页模式的片段,因为有几个控制台日志。
我设法使用路口观察器做到了这一点。我的要求是检查该元素是否至少在一秒钟内占 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);
每当某个 css class 的内容块可见 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% 正确,但要点是我尝试:
- 测试 scroll/resize 上每个 myClass 元素的可见性
- 如果有一个可见,请等待 5 秒并再次检查同一元素。
问题是,当 setTimeout 运行 isInViewport 时,元素未定义。也许 jQuery 的 .each 和 setTimeout 不匹配?
您可以尝试使用 Waypoints,它是一个库,可让您确定元素何时进入或离开视口。您向它传递一个接受方向参数的事件处理程序。方向告诉您被跟踪的元素是进入还是退出屏幕。一旦您检测到元素已进入屏幕,然后启动计时器。如果您没有看到元素退出视口的时间和事件,那么您就知道它已经在屏幕上显示了那段时间。
您可以使用此函数检查元素是否在视口中(来自 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>
我使用 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>
请运行全页模式的片段,因为有几个控制台日志。
我设法使用路口观察器做到了这一点。我的要求是检查该元素是否至少在一秒钟内占 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);