Reputation: 85
I am building STEEP. We are using infinite scroll to load in just 8 videos at a time when scrolling the page.
However, the script that is handling the 'play' of the videos only works on the first 8, not the items that are paginated. When logging 'videos' I only get the list with the first 8 even after the page is scrolled. So we suspect that "document.querySelectorAll" somehow isn't fetching the new elements. I tried looping the whole thing every second and that actually worked – while obviously not being ideal at all.
The difference between Safari and Chrome is also notable. In Safari we don't even see the videos taking up any space in the layout.
SCRIPT:
const videos = document.querySelectorAll('video');
videos.forEach(video => {
video.addEventListener("mouseover", function () {
var posterTime = this.currentTime;
var videoDuration = this.duration;
this.currentTime = 0;
this.play()
})
video.addEventListener("mouseleave", function() {
let src = this.querySelector('source').src;
let time = (src.split('#')[1] || 't=0').split('=')[1];
this.currentTime = time;
this.pause();
})
})
HTML
<video id="video" width="100%" height="auto" loop muted playsinline preload="metadata">
<source src="<CMS URL IS SET HERE>" type="video/mp4">
</video>
Note: We are not very experienced with coding. We use Webflow in combination with a bit of custom code.
Upvotes: 1
Views: 1108
Reputation: 90277
querySelectorAll
selects all the existing elements in the page at the time it's run, matching the provided selector. It works in every single browser, every single time. If you're only getting 8 results, you definitely only have 8 <video>
tags in DOM at that particular time.
Infinite scroll, on the other hand, improves performance by only rendering the currently visible elements on the screen and creating new elements as necessary, as the user scrolls. Some implementations even go as far as replacing the elements above the currently visible area with empty containers, once you scrolled past them, for the same exact reason: to improve overall page rendering speed and UX performance.
So, obviously, querySelectorAll
is not the right way to get all videos matching the current page's filter criteria, on a page featuring infinite scroll plugin. In general, when you want to do something to all your data, you'd have to get it from the same source your DOM is getting it from (probably some API endpoint), unaltered, and not rely on what's currently displayed/present in DOM.
In your particular case, you don't seem to be interested in the videos per-se, you simply want some functionality: when a <video>
tag is hovered, it should start playing. When it's not hovered, it should be paused.
To get this functionality working on all present and future <video>
tags in your DOM, rather than hunting down <video>
tag, adding events to it and keeping track which ones has the events and which one does not, whenever you update DOM (which could go south in so many ways), you could use a cleaner and easier solution: add the events only once, on a stable parent container of all <video>
s and rely on the events' ability to bubble
(propagate) from children to parent.
Given your current markup, this should work:
const list = document.querySelector('.collection-list');
const isVideoPlaying = video => !!(
video && video.currentTime > 0 &&
!video.paused && !video.ended && video.readyState > 2
);
const playVideo = el => !isVideoPlaying(el) && el.play();
const pauseVideo = el => isVideoPlaying(el) && el.pause();
const updateVideos = () => {
[...document.querySelectorAll('.collection-list video:hover')]
.forEach(playVideo);
[...document.querySelectorAll('.collection-list video:not(:hover)')]
.forEach(pauseVideo);
}
list.addEventListener('mouseover', updateVideos);
list.addEventListener('mouseout', updateVideos);
See it working:
const list = document.querySelector('.collection-list');
const isVideoPlaying = video => !!(
video && video.currentTime > 0 &&
!video.paused && !video.ended && video.readyState > 2
);
const playVideo = el => !isVideoPlaying(el) && el.play();
const pauseVideo = el => isVideoPlaying(el) && el.pause();
const updateVideos = () => {
[...document.querySelectorAll('.collection-list video:hover')]
.forEach(playVideo);
[...document.querySelectorAll('.collection-list video:not(:hover)')]
.forEach(pauseVideo);
}
list.addEventListener('mouseover', updateVideos);
list.addEventListener('mouseout', updateVideos);
.collection-list {
max-width: 1200px;
margin: 0 auto;
}
body {
padding: 1rem;
}
<link href="https://assets.website-files.com/619954581893d62b1fb78f71/css/steep2.webflow.cf847148b.css" rel="stylesheet" />
<div fs-cmsload-element="list" fs-cmsload-mode="infinite" fs-cmsload-resetix="true" data-w-id="56057e90-351e-68a6-c072-7ce8486cec5c" role="list" class="collection-list w-dyn-items">
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/dedcool" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61c4a7d8d44cb30efdf3d5ae_34640529_2175246485823867_4442191443397705728_n.jpg_nc_cat100ccb1-5_nc_sidb2f0f4_nc_ohcZFe9YCoyZUAAX-SS6lV_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">DedCool</div>
<div class="story-count-con">
<div class="story-brand-number">7</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661071220.sd.mp4?s=3e887051f981a6e16d574adf3a6027665dc7fdbd&profile_id=165#t=0" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="163-dedcool" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/airbnb" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61c4a7e80668cf713e24e60f_28167842_10156245930652458_7073556191774351242_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcjEnmlfOp6gMAX_mlIjU_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Airbnb</div>
<div class="story-count-con">
<div class="story-brand-number">6</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661071968.sd.mp4?s=ecfc549785d18c2a37540057dc64caf93c8c424f&profile_id=165#t=2.8" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="162-airbnb" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/airbnb" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61c4a7e80668cf713e24e60f_28167842_10156245930652458_7073556191774351242_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcjEnmlfOp6gMAX_mlIjU_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Airbnb</div>
<div class="story-count-con">
<div class="story-brand-number">6</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661071982.sd.mp4?s=825b698c06816978e5103f7d340cd74dfd717f59&profile_id=165#t=1.2" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="161-airbnb" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/airbnb" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61c4a7e80668cf713e24e60f_28167842_10156245930652458_7073556191774351242_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcjEnmlfOp6gMAX_mlIjU_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Airbnb</div>
<div class="story-count-con">
<div class="story-brand-number">6</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661071994.sd.mp4?s=039d1aac52f67cb3daadb2c5d84abdad2e6fee39&profile_id=165#t=0.2" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="160-airbnb" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/ajandsmart" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61c4a80c27b764720e65adf6_18814065_1547343241944009_2625897978373999509_n.png_nc_cat110ccb1-5_nc_sidb2f0f4_nc_ohcWVZ7Y6NJBZ0AX-6z-gZ_nc_htscontent-cph2-1.png")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">AJ&Smart</div>
<div class="story-count-con">
<div class="story-brand-number w-condition-invisible">1</div>
<div class="story-brand-number w-condition-invisible">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661072008.sd.mp4?s=30e7bcb5f82f16a75f19e0e67a187cebf5ce8cfc&profile_id=165#t=0.1" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="159-ajandsmart" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/artlist" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61cd0f1d1f33167c407ea00c_34455623_2133014916712600_4428653032170848256_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcBKUq7rLNhtkAX-UGFSB_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Artlist</div>
<div class="story-count-con">
<div class="story-brand-number">4</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661072020.sd.mp4?s=d4c90928326240a473cbb25969f6e528fdb01891&profile_id=165#t=0.37" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="158-artlist" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/artlist" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61cd0f1d1f33167c407ea00c_34455623_2133014916712600_4428653032170848256_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcBKUq7rLNhtkAX-UGFSB_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Artlist</div>
<div class="story-count-con">
<div class="story-brand-number">4</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661072030.sd.mp4?s=a3adeab812784f92f74b39abc975060043b64e5f&profile_id=165#t=5.3" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="157-artlist" /> --></div>
</div>
<div role="listitem" class="story-item w-dyn-item">
<div data-w-id="71b6ca01-b466-9c84-94b3-c4ff009c6ec7" class="story-container" style="opacity: 1;">
<a href="/brand/artlist" class="story-furniture w-inline-block">
<div style="background-image:url("https://assets.website-files.com/6199556378f2212c78d5884d/61cd0f1d1f33167c407ea00c_34455623_2133014916712600_4428653032170848256_n.jpg_nc_cat1ccb1-5_nc_sidb2f0f4_nc_ohcBKUq7rLNhtkAX-UGFSB_nc_htscontent-cph2-1.jpeg")"
class="brand-logo"></div>
<div class="story-furniture-txt">
<div class="story-brand-txt">Artlist</div>
<div class="story-count-con">
<div class="story-brand-number">4</div>
<div class="story-brand-number">stories</div>
</div>
</div>
</a>
<div class="story-video-embed w-embed"><video class="video" width="100%" height="auto" loop="" muted="" playsinline="" preload="metadata">
<source src="https://player.vimeo.com/external/661072039.sd.mp4?s=2de96ddc6ca1d36541f4076ff14fd508c38fa61c&profile_id=165#t=12.8" type="video/mp4">
</video></div>
<div class="progress__con" style="opacity: 0;">
<div id="progress-bar" class="progress-bar" style="animation: 0s linear 0s 1 normal none running reset;"></div>
</div>
</div>
<div class="jetboost-embed w-embed">
<!-- <input type="hidden" class="jetboost-list-item" value="156-artlist" /> --></div>
</div>
</div>
I'm adding events to the parent list, instead of adding them to each child. The events used are mouseover
and mouseout
(these two bubble, as opposed to mouseenter
and mouseleave
, which do not bubble).
What the handlers attached do is simple: query our list for any existing <video>
elements. We play any hovered non-playing ones and pause any playing non-hovered ones.
Upvotes: 2
Reputation: 337701
querySelectorAll()
will only find elements in the DOM at the point it runs.
Given that you say that the other pages of videos load as the user scrolls it sounds like you're using AJAX to dynamically add them to the DOM at this time. As such, you just need to put the JS logic shown in your question in to a function and then call it both when the page loads and also when the AJAX completes and the new video content has been added to the DOM.
The logic would look something like this:
let attachVideoEventHandlers = container => {
container.querySelectorAll('video').forEach(video => {
video.addEventListener("mouseover", function() {
var posterTime = this.currentTime;
var videoDuration = this.duration;
this.currentTime = 0;
this.play();
})
video.addEventListener("mouseleave", function() {
let src = this.querySelector('source').src;
let time = (src.split('#')[1] || 't=0').split('=')[1];
this.currentTime = time;
this.pause();
})
})
}
document.addEventListener('DOMContentLoaded', () => {
attachVideoEventListeners(document);
document.addEventListener('scroll', e => {
// your logic here to detect scrolling to the bottom of the page...
// make AJAX request to fetch new video data
// update DOM to render new videos
let container = /* get containing element for new page of videos just added to the DOM */
attachVideoEventListeners(container);
}
});
Upvotes: 3