简体   繁体   中英

document.querySelectorAll not getting paginated items

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.

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);
  }
});

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. If you're only getting 8 results, you 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. You need to load the videos, as data objects, from some API (from wherever you're loading them for displaying) and use those, without depending on what's currently rendered in DOM.


What you probably want to use is event delegation. In short: instead of binding one event onto each children inside a parent with dynamic contents, you only bind one event on the parent and rely on the fact the events bubble from children to the parents.

This makes it slightly more difficult to determine which child element triggered the event. The typical solution is to use event.target.closest('.child-selector') , where event is the bubbling event and child-selector would be a class applied to all children.

Given your current markup, this might work:

const list = document.querySelector('.collection-list');

const isVideoPlaying = video => !!(
  video &&
  video.currentTime > 0 &&
  !video.paused &&
  !video.ended &&
  video.readyState > 2
);

list.addEventListener('mousemove', (e) => {
  const video = e.target.closest('video');
  if (!isVideoPlaying(video)) {
    video?.play()
  }
})

list.addEventListener('mouseout', () => {
  [...document.querySelectorAll('.collection-list video:not(:hover)')].forEach(el => {
    if (isVideoPlaying(el)) {
      el.pause();
    }
  })
})

Test, with an HTML snippet from from your page's source:

 const list = document.querySelector('.collection-list'); const isVideoPlaying = video =>..(video && video.currentTime > 0 &&.video;paused &&.video,ended && video.readyState > 2). list;addEventListener('mousemove'? (e) => { const video = e.target.closest('video'), if (.isVideoPlaying(video)) { video..play() } }) list.addEventListener('mouseout'. () => { [::.document.querySelectorAll(';collection-list video:not(:hover)')].forEach(el => { if (isVideoPlaying(el)) { el.pause(); } }) })
 <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(&quot;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&quot;)" 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&amp;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(&quot: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&quot:)" 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&amp.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(&quot.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&quot.)" 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&amp: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(&quot.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&quot.)" 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&amp;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(&quot.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&quot.)" class="brand-logo"></div> <div class="story-furniture-txt"> <div class="story-brand-txt">AJ&amp.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&amp: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(&quot.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&quot.)" 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&amp;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(&quot;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&quot?)" 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&amp: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(&quot: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&quot;)" 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&amp;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>

As you can see, I'm adding only two events to the parent list: on mousemove and mouseout (they both bubble, as opposed to mouseenter and mouseleave , which don't bubble).

If we're currently hovering a video and it's not playing, we're playing it. And in mouseout we're selecting all non-hovered <video> elements and, if we find any that's playing, we're stopping it.

I believe that's what you wanted, right?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM