Search code examples
javascriptjquerybackend

JS, issue running hover video playback functions independently on multiple cards


<html>
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
  <link rel="stylesheet" 
  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> 
  <title>Game Cards App</title>
  <link rel="icon" type="image/png" href="https://cdn1.iconfinder.com/data/icons/entertainment-events-hobbies/24/card-game-cards-hold-512.png">
  
<style>

#main-content {
  display: none;
}

* {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-flow: column wrap;
  background: radial-gradient(circle, rgba(7, 50, 22, 255) 0%, rgba(0, 0, 0, 255) 100%);
  animation: shine 4s linear infinite;
  color: white;
  font-family: "Lato";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
  max-width: 800px;
  width: 100%;
  margin: 0 auto;
  padding: 15px;
  text-align: center;
  overflow-x: hidden;
}

.card {
  float: left;
  position: relative;
  width: calc(33.33% - 30px + 9.999px);
  height: 340px;
  margin: 0 30px 30px 0;
  perspective: 1000;
 
}
.card:first-child .card__front {
  background:#5271C2;
}

.card__front img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}



.card:first-child .card__num {
  text-shadow: 1px 1px rgba(52, 78, 147, 0.8)
}
.card:nth-child(2) .card__front {
  background:#35a541;
}
.card:nth-child(2) .card__num {
  text-shadow: 1px 1px rgba(34, 107, 42, 0.8);
}
.card:nth-child(3) {
  margin-right: 0;
}
.card:nth-child(3) .card__front {
  background: #bdb235;
}
.card:nth-child(3) .card__num {
  text-shadow: 1px 1px rgba(129, 122, 36, 0.8);
}
.card:nth-child(4) .card__front {
  background: #db6623;
}
.card:nth-child(4) .card__num {
  text-shadow: 1px 1px rgba(153, 71, 24, 0.8);
}
.card:nth-child(5) .card__front {
  background: #3e5eb3;
}
.card:nth-child(5) .card__num {
  text-shadow: 1px 1px rgba(42, 64, 122, 0.8);
}
.card:nth-child(6) .card__front {
  background: #aa9e5c;
}
.card:nth-child(6) .card__num {
  text-shadow: 1px 1px rgba(122, 113, 64, 0.8);
}
.card:last-child {
  margin-right: 0;
}
.card__flipper {
  cursor: pointer;
  transform-style: preserve-3d;
  transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
  border: 3.5px solid rgba(255, 215, 0, 0.6); 
  background-image: linear-gradient(45deg, rgba(255, 215, 0, 0.5), transparent, rgba(255, 215, 0, 0.5)); 
}
.card__front, .card__back {
  position: absolute;
  backface-visibility: hidden;
  top: 0;
  left: 0;
  width: 100%;
  height: 340px;
}
.card__front {
  transform: rotateY(0);
  z-index: 2;
  overflow: hidden;
}
.card__back {
  transform: rotateY(180deg) scale(1.1);
  background: linear-gradient(45deg, #483D8B, #301934, #483D8B, #301934);
  display: flex;
  flex-flow: column wrap;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);

}
.card__back span {
  font-weight: bold; /* Metni kalın yap */
  color: white; /* Beyaz renk */
  font-size: 16px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.card__name {
  font-size: 32px;
  line-height: 0.9;
  font-weight: 700;
}
.card__name span {
  font-size: 14px;
}
.card__num {
  font-size: 100px;
  margin: 0 8px 0 0;
  font-weight: 700;
}
@media (max-width: 700px) {
  .card__num {
    font-size: 70px;
  }
}
@media (max-width: 700px) {
  .card {
    width: 100%;
    height: 290px;
    margin-right: 0;
    float: none;
  }
  .card .card__front,
  .card .card__back {
    height: 290px;
    overflow: hidden;
  }
}

/* Demo */
main {
  text-align: center;
}
main h1, main p {
  margin: 0 0 12px 0;
}
main h1 {
  margin-top: 12px;
  font-weight: 300;
}

.fa-github {
  color: white; 
  font-size: 50px;
  margin-top: 8px; 
}

.tm-container {
  display: flex;
  justify-content: center;
  align-items: center; 
  
}

.tm-letter {
 
  display:inline-block;
  font-size:30px;
  margin: 0 5px;
  margin-top: 10px;
  opacity: 0;
  transform: translateY(0);
  animation: letter-animation 6s ease-in-out infinite;
}

@keyframes letter-animation {
  0%, 100% {
    opacity: 1;
    transform: translateY(0);
  }
  10%, 40%, 60%, 90% {
    opacity: 1;
    transform: translateY(-10px);
  }
  20%, 80% {
    opacity: 1;
    transform: translateY(0);
  }
}

#m-letter {
  animation-delay: 1.5s;
}

a {
    position: relative;
    display: inline-block;
    padding: 0px;
}

a::before {
    content: '';
    position: absolute;
    top: 50%; /* Orta konumu */
    left: 50%; /* Orta konumu */
    transform: translate(-50%, -50%); 
    width: 50px;
    height: 45px;
    border-radius: 50%; /* Eğer bir daire şeklinde efekt isteniyorsa */
    box-shadow: 0 0 8px 4px rgba(110, 110, 110, 0.8); 
    filter: blur(4px) brightness(1.5); 
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
    z-index: -1;
}

a:hover::before {
    opacity: 1;
  
}


body.hoverEffect {
      background: radial-gradient(circle at center, #000000, #000033, #000066, #1a1a1a);
}

#gameCard { 
    width: 300px;
    height: 450px;
    margin: 50px auto;
    padding: 20px;
    border-radius: 15px;
    box-shadow: 
    0 0 50px 10px #FFD700,  
    0 0 100px 20px #0000FF, 
    0 0 150px 30px #000033; 
    background: rgba(0,0,0,0.7);  
    color:#FFD700;   
    text-align: center;
    border: 3px solid #FFD700; 
}

#gameCardLink span {
  font-size: 18px;
  margin-right: 5px; 
  font-weight: bold; 
}

#gameCardLink span:last-child {
  font-size: 0.79em; 
  vertical-align: super; 
  opacity: 0.9;
  font-weight: bold; 
  text-shadow: 2px 2px 2px rgba(0, 0, 0, 0.5); 
}


#loading-animation {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image:url();
  background-repeat: no-repeat;
  background-size: cover ;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 9999;
 }

.loader {

  border-top: 9px solid #00a2ed;
  border-radius: 80%;
  width: 12vw; 
  height: 12vw; 
  animation: spin 2s linear infinite; 
  position: absolute; 
  left: 44%; 
  top: 46%; /
  transform: translate(-50%, -50%) rotate(0deg); /* Yuvarlak halkanın tam ortasında olması için bu dönüşümü kullanın. */
  }

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

</style> 
</head>



<body>

<div id="loading-animation">
  <div class="loader"></div>
</div>

<div id="main-content">

  <div class="tm-container">
    <div class="tm-letter" id="t-letter">T</div>
    <div class="tm-letter" id="m-letter">M</div>
  </div>


<audio id="flipSound" preload="auto">
  <source src="https://cdn.freesound.org/previews/321/321114_2776777-lq.ogg" type="audio/wav">
</audio>

<main>
   <div id="gameCardLink">
      <span>G</span>
      <span>a</span>
      <span>m</span>
      <span>e</span>
      <span> </span> <!-- Boşluk eklemek için span ekledik -->
      <span> </span>
      <span>C</span>
      <span> </span>
      <span>a</span>
      <span> </span>
      <span>r</span>
      <span> </span>
      <span>d</span>
      <span> </span>
      <span>s</span>
      <span>®</span>
   </div>
  <p><a href="https://github.com/murattasci06"><i class="fab fa-github"></i></a></p>
</main>


<ul>
  <li id='spider' class="card" >
    <div class="card__flipper">
      <div class="card__front">
        <img src="https://gecbunlari.com/wp-content/uploads/2021/12/Spiderman-No-Way-Home.jpg" alt="Spiderman">
        <p class="card__name"><span>Marvel</span><br>Spiderman</p>
        <p class="card__num">1</p>
      </div>
  
      
      <iframe class="frame" 
      width="225"
      height="340"
      src="https://www.youtube.com/embed/JfVOs4VSpmA?autoplay=1&mute=1&vq=hd1080"
      title="YouTube video player"
      frameborder="0"
      allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
      allowfullscreen
      ></iframe>

      
      <div class="card__back">
        <svg height="180" width="180">
        <circle cx="90" cy="90" r="65" stroke="#514d9b" stroke-width="35"  />
        <!-- Dış dairenin kenarı (yeşil) -->
         <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>1.89 Bil. $</span>
      </div>
    </div>
  </li>
 
 
  <li id='batman' class="card">
    <div class="card__flipper">
       <div class="card__front">
         <img src="https://i.pinimg.com/736x/1e/f1/3d/1ef13dfa4b7b8c131302e242d1ec48d7.jpg" alt="Batman">
         <p class="card__name"><span>DC</span><br>Batman</p>
        <p class="card__num">2</p>
      </div>
      
        <iframe class="frame" 
        width="225"
        height="340"
        src="https://www.youtube.com/embed/mqqft2x_Aa4?autoplay=1&mute=1&vq=hd1080"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowfullscreen
        ></iframe>

      
      <div class="card__back">
        <svg height="180" width="180">
          <circle cx="90" cy="90" r="65" stroke="#35a541" stroke-width="35"/>
          <!-- Dış dairenin kenarı (yeşil) -->
          <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>771 Mil. $</span>
      </div>
    </div>
  </li>
  
  <li id='guard' class="card">
    <div class="card__flipper">
       <div class="card__front">
        <img src="https://wallpapercave.com/wp/wp12279011.jpg" alt="Guardians_of_the_Galaxy_Vol_3">
        <p class="card__name"><span>Marvel</span><br>Guardians_of_the_Galaxy_Vol_3</p>
        <p class="card__num">3</p>
        </div>
        <iframe class="frame" 
        width="225"
        height="340"
        src="https://www.youtube.com/embed/u3V5KDHRQvk?autoplay=1&mute=1&vq=hd1080"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowfullscreen
        ></iframe>
        
     
      <div class="card__back">
        <svg height="180" width="180">
          <circle cx="90" cy="90" r="65" stroke="#bdb235" stroke-width="35"/>
         <!-- Dış dairenin kenarı (yeşil) -->
          <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>845.4 Mil. $</span>
      </div>
    </div>
  </li>
   
  
  <li id='shazam' class="card">
    <div class="card__flipper">
       <div class="card__front">
       <img src="https://wallpaperaccess.com/full/8940499.jpg" alt="Shazam">
        <p class="card__name"><span>DC</span><br>Shazam2</p>
        <p class="card__num">4</p>
        </div>
        <iframe class="frame" 
        width="225"
        height="340"
        src="https://www.youtube.com/embed/AIc671o9yCI?autoplay=1&mute=1&vq=hd1080"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowfullscreen
        ></iframe>
        
        
     
      <div class="card__back">
        <svg height="180" width="180">
          <circle cx="90" cy="90" r="65" stroke="#db6623" stroke-width="35"/>
         <!-- Dış dairenin kenarı (yeşil) -->
          <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>462.5 Mil. $</span>
      </div>
    </div>
  </li>

  <li id='flash' class="card">
    <div class="card__flipper">
       <div class="card__front">
        <img src="https://images2.alphacoders.com/131/1316679.jpeg" alt="Flash">
        <p class="card__name"><span>DC</span><br>Flash</p>
        <p class="card__num">5</p>
        </div>
        <iframe class="frame" 
        width="225"
        height="340"
        src="https://www.youtube.com/embed/hebWYacbdvc?autoplay=1&mute=1&vq=hd1080"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowfullscreen
        ></iframe>
        
        
     
      <div class="card__back">
        <svg height="180" width="180">
          <circle cx="90" cy="90" r="65" stroke="#3e5eb3" stroke-width="35"/>
          <!-- Dış dairenin kenarı (yeşil) -->
          <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>560.2 Mil. $</span>
      </div>
    </div>
  </li>
 
  <li id='strange' class="card">
    <div class="card__flipper">
       <div class="card__front">
        <img src=" https://images3.alphacoders.com/121/1213553.jpg" alt="Dr_Strange_2">
        <p class="card__name"><span>Marvel</span><br>Dr_Strange_2</p>
        <p class="card__num">6</p>
         </div>
        <iframe class="frame" 
        width="225"
        height="340"
        src="https://www.youtube.com/embed/aWzlQ2N6qqg?autoplay=1&mute=1&vq=hd1080"
        title="YouTube video player"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowfullscreen
        ></iframe>
        
      
      <div class="card__back">
        <svg height="180" width="180">
          <circle cx="90" cy="90" r="65" stroke="#aa9e5c" stroke-width="35"/>
          <!-- Dış dairenin kenarı (yeşil) -->
          <circle cx="90" cy="90" r="83" fill="none" stroke="rgba(7, 50, 22, 255)" stroke-width="1" />
        </svg>
        <span>955.8 Mil. $</span>
      </div>
    </div>
  </li>
</ul>

</div>

</body>


<script>
var Flipper = (function() {
  var card = $('.card');
  var flipper = card.find('.card__flipper');
  var win = $(window);
  
  var flip = function() {
    var thisCard = $(this);
    var thisFlipper = thisCard.find('.card__flipper');
    var offset = thisCard.offset();
    var xc = win.width() / 2;
    var yc = win.height() / 2;
    var docScroll = $(document).scrollTop();
    var cardW = thisCard.outerWidth() / 2;
    var cardH = thisCard.height() / 2;

    var transX = xc - offset.left - cardW;
    var transY = docScroll + yc - offset.top - cardH;
   // if (offset.top > card.height()) transY = docScroll - offset.top + cardH;
    if (win.width() <= 700) transY = 0;
    
    if (card.hasClass('active')) unflip();
        
    thisCard.css({'z-index': '3'}).addClass('active');
    
    thisFlipper.css({
      'transform': 'translate3d(' + transX + 'px,' + transY + 'px, 0) rotateY(180deg) scale(1)',
      '-webkit-transform': 'translate3d(' + transX + 'px,' + transY + 'px, 0) rotateY(180deg) scale(1)',
      '-ms-transform': 'translate3d(' + transX + 'px,' + transY + 'px, 0) rotateY(180deg) scale(1)'
    }).addClass('active');
    
    
    return false;
  };
  
  var unflip = function(e) {
    card.css({'z-index': '1'}).removeClass('active');
    flipper.css({
      'transform': 'none',
      '-webkit-transform': 'none',
      '-ms-transform': 'none'
    }).removeClass('active');
  };
  
  var bindActions = function() {
    card.on('click', flip);
    win.on('click', unflip);
  }
  
  var init = function() {
    bindActions();
  };
  
  return {
    init: init
  };
  
}());
Flipper.init();
  
</script>



<script>
<!-- HOVER FOR TRAILER -->

var hoverTimeout;

function onHover() {
    const currentCard = $(this); 

    hoverTimeout = setTimeout(() => {
        currentCard.find('.card__front').hide();
        currentCard.find('.iframe').show();
        var src = currentCard.find('.iframe').attr("src");
        currentCard.find('.iframe').attr("src", src);

     
        currentCard.find('.iframe').on('click', function() {
            $(this).requestFullscreen();
        });

    }, 5000); // 5000 milliseconds (5 seconds)
 
}
function onHoverTime() {
    clearTimeout(hoverTimeout); // Clear the timeout to prevent actions if the user moves away before 5 seconds

    $(this).find('.card__front').show();
    $(this).find('.iframe').hide();
    var src = $(this).find('.iframe').attr("src");
    if (src) {
        $(this).find('.iframe').attr("src", src.replace('?autoplay=1', ''));
    }
};


function setupHoverEffect(cardId) {
  console.log(`setupHoverEffect function is called for ${cardId}`);
  $(`#${cardId}`).hover(function() {
    // Call the existing onHover function when the mouse hovers over the card
    onHover.call(this);
  }, function() {
    // Call the existing onHoverTime function when the mouse leaves the card
    onHoverTime.call(this);
  });
}

setupHoverEffect("batman");
setupHoverEffect("flash");
setupHoverEffect("spider");
setupHoverEffect("shazam");
setupHoverEffect("flash");
setupHoverEffect("strange");



</script>



<script>
var cardFlags = {};

$(document).ready(function() {
  var flipSound = document.getElementById("flipSound");
  $(".card__front").click(function() {

    flipSound.currentTime = 0;
    flipSound.play();

  });

  
  $(".card").click(function() {
    var card = $(this);
    var cardId = card.find(".card__num").text();

    
    // Check if the card is not already flipping to avoid multiple flips
    if (!card.hasClass('flipping')) {
      card.addClass('flipping');

      // Check if the front side is facing the viewer
      if (!card.hasClass("flipped")) {
       


        if (!cardFlags[cardId]) {
          startAnimation(div); 

          cardFlags[cardId] = true; 
          card.addClass("flipped");
        }
        
        // Adding canvas to back-side 
        var div = document.querySelector('.flipped .card__flipper.active .card__back');
        var canvas = document.getElementsByClassName('p5Canvas')[0];
        if (div && canvas) {
          div.appendChild(canvas);
          canvas.style.position = 'absolute';
        }
      } else {
       

        card.removeClass("flipped");
        
      }

      setTimeout(function() {
        card.removeClass('flipping');
      }, 1000); 
    }
  });

  // Prevent sound on back side click
  $(".card__back").click(function(e) {
    e.stopPropagation();
  });
});

</script>


<script>
    // Body's hoover effect
    document.getElementById("gameCardLink").addEventListener("mouseover", function() {
        document.body.classList.add("hoverEffect");
    });

    document.getElementById("gameCardLink").addEventListener("mouseout", function() {
        document.body.classList.remove("hoverEffect");
    });
</script>




<script>

// Portal effect

</script>



<script>
// Hiding and showing of loading animation
  function hideLoadingAnimation() {

  var loadingAnimation = document.getElementById("loading-animation");
  var mainContent = document.getElementById("main-content");
  loadingAnimation.style.display = "none";
  mainContent.style.display = "block";
}
window.onload = function() {

  hideLoadingAnimation();
};

</script>



<script> 
    // The conditions of the hover function.
    $('.card').on( "mouseenter", onHover).on( "mouseleave", onHoverTime );
    
    // hover events removing and assigning
    $(document.body).on('click', function(){
        if ($("#batman").off( "mouseenter", onHover)){
            $('#batman, #flash').hover(onHover, onHoverTime);
            console.log('yes');
            } })
            
         // overlap boundary conditions for hover on off
    $(".card").click(function(){
    let cord = document.getElementsByClassName('active')[1].style.transform; 
    [cx,cy,...rest]= cord.slice(12).split('px, ');
    console.log(`cx: ${cx}, cy: ${cy}`);
    if ((cy<=300)&&(cy>=100)||((cy>(-300))&&(cy<(-60)))){

        $("#batman").off( "mouseenter", onHover);
        $("#flash").off( "mouseenter", onHover);
        
        console.log('offed');
        let hoverTimeout;
    }
    else{
        console.log('on');
        $('#batman, #flash').hover(onHover, onHoverTime);
    }
    
});     
                
</script>

    
</html>

I currently have 6 cards with ids: spider, batman, guard, shazam, flash, strange. (I plan to increase the number) For these cards, I have common onHover() and onHoverTime() functions that play video trailers with hover and reset them. I want it to work independently for each card. If I need to give a little more detail, I can say that the trailer is played when you wait 5 seconds with the mouse on the front of each card, but the longer you wait on a card, the more the trailer progresses on that card when the hover event occurs on another card. So they work as a whole.

However, what I want is for each card's hover trailer functions to work card-specific and not affect each other. (The trailer should restart from 0:00 on every hover event of each card.)

I thought adding of the following approach but it doesn't work the way I want:

function setupHoverEffect(cardId) {
  console.log(`setupHoverEffect function is called for ${cardId}`);
  $(`#${cardId}`).hover(function() {
    // Call the existing onHover function when the mouse hovers over the card
    onHover.call(this);
  }, function() {
    // Call the existing onHoverTime function when the mouse leaves the card
    onHoverTime.call(this);
  });
}
setupHoverEffect("spider");
setupHoverEffect("batman");
setupHoverEffect("guard");
setupHoverEffect("shazam");
setupHoverEffect("flash");
setupHoverEffect("strange");

Additionally, I thought it wouldn't be unmanageable if the number of cards increased so much. It may not be correct to repeat setupHoverEffect() for new cards added. How can I solve this problem?

The link of full code: https://drive.google.com/file/d/1ptX0FanoKVRUC6ni5J7_2TYUNyYuNEl_/view

visual representation of the problem:

enter image description here

enter image description here


Solution

  • There are so many issues happening, but I will try to address the most important ones. First thing I notice is that all the videos are set to be autoplay by default, so they all run when the page is loaded:

    <iframe class="frame" 
          width="225"
          height="340"
          src="https://www.youtube.com/embed/JfVOs4VSpmA?autoplay=1&mute=1&vq=hd1080"
          title="YouTube video player"
          frameborder="0"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
          allowfullscreen
          ></iframe>
    

    In the above code, see that there is query params in the src attribute autoplay=1, we should first remove them from all the iframes.

    The next problem is that you query the DOM wrong:

    function onHover() {
        const currentCard = $(this); 
    
        hoverTimeout = setTimeout(() => {
            currentCard.find('.card__front').hide();
            currentCard.find('.iframe').show();
            var src = currentCard.find('.iframe').attr("src");
    ...
    

    The class .iframe doesn't exist, since you assigned the iframes with .frame class instead. But to fix this, we can just select the DOM by its tag name, e.g.:

    currentCard.find('iframe').show();
    

    And then, since we have removed the autoplay query params, we need to readd them when hover event happens, we can trigger the play by appending autoplay=1 to the query params, but when hover event happens like this inside the onHover() function:

    currentCard.find('iframe').attr("src", src + '&autoplay=1');
    

    Then finally, we need to fix the clearTimeout, it seems like the setTimeouts are triggered multiple times, i'm not sure why, but basically we can store the timeout id to an object so that we can store and clear all of them:

    var hoverTimeout = {}; // an object to store timeout id
    

    And then update the onHover() function:

    function onHover() {
      const currentCard = $(this);
    
      if (hoverTimeout[currentCard.attr('id')] === undefined) {
        hoverTimeout[currentCard.attr('id')] = []; // initialize timeout id array according to element id
      }
    
      hoverTimeout[currentCard.attr('id')].push(setTimeout(() => {
        currentCard.find('.card__front').hide();
        currentCard.find('iframe').show();
        var src = currentCard.find('iframe').attr("src");
        currentCard.find('iframe').attr("src", src + '&autoplay=1');
    
        currentCard.find('iframe').on('click', function() {
          $(this).requestFullscreen();
        });
    
      }, 5000)); // 5000 milliseconds (5 seconds)
    }
    

    Finally, we can update the onHoverTime function to this, so we can clear the timeout ids correctly:

    function onHoverTime() {
      while (hoverTimeout[$(this).attr('id')].length > 0) {
        clearTimeout(hoverTimeout[$(this).attr('id')][0]); // Clear the timeout to prevent actions if the user moves away before 5 seconds
        hoverTimeout[$(this).attr('id')].shift();
      }
    
      $(this).find('.card__front').show();
      $(this).find('iframe').hide();
      var src = $(this).find('iframe').attr("src");
      if (src) {
        $(this).find('iframe').attr("src", src.replace('?autoplay=1', ''));
      }
    }
    

    Here is the working fiddle.