Peter Wong
Peter Wong

Reputation: 23

Multiple Vimeo video in multiple modals

I have several popup modals each with it's own video in autoplay.

Each popups work and close correctly and video autoplays but when I click on the second one, the first one autoplays in the background and the second one doesn't autoplay.

Not sure what I am doing wrong. I have been working on it at:Codepen example

// Get the button that opens the modal
var btn = document.querySelectorAll("a.modal-button");

// All page modals
var modals = document.querySelectorAll(".modal");

// Get the <span> element that closes the modal
var spans = document.querySelectorAll(".close");


// When the user clicks the button, open the modal
let iframe = document.querySelector('iframe');
let player = new Vimeo.Player(iframe);

for (var i = 0; i < btn.length; i++) {
  btn[i].onclick = function (e) {
    e.preventDefault();
    modal = document.querySelector(e.target.getAttribute("href"));
    modal.style.display = "block";
        player.play();
  };
}

// When the user clicks on <span> (x), close the modal
for (var i = 0; i < spans.length; i++) {
  spans[i].onclick = function () {
    for (var index in modals) {
      if (typeof modals[index].style !== "undefined")
        modals[index].style.display = "none";
        player.pause();     
    }
  };
}

// When the user clicks anywhere outside of the modal, close it
window.onclick = function (event) {
  if (event.target.classList.contains("modal")) {
    for (var index in modals) {
      if (typeof modals[index].style !== "undefined")
        modals[index].style.display = "none";
                player.pause();
    }
  }
};
.modal {
    display: none;
    padding-top: 100px;
    overflow: auto;
    background-color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0.4);
    margin-bottom: auto;
    padding: 40px;
    border-radius: 8px;
    position: fixed;
    z-index: 9999999;
}
.modal-content {
    background-color: #fff;
    padding: 1rem;
    border: 1px solid #333;
    padding: 40px;
    border-radius: 8px;
    display: flex;
        align-items: center;
        justify-content: center;
        margin: auto;
}

.close {
    position: absolute;
    right: 0;
    top: 0;
    transform: rotate(-45deg);
    font-size: 28px;
    font-weight: bold;
}

iframe {
      width: 100%;
    height: 100%;
}
<script src="https://player.vimeo.com/api/player.js"></script>

<div class="modal" id="modal1">
    <div class="modal-content">
  <iframe src="https://player.vimeo.com/video/1053959997?title=0&amp;byline=0&amp;portrait=0&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" width="1080" height="1620" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media" title="long - dynamic content"></iframe>
  </div>
</div>

<div class="modal" id="modal2">
  <div class="modal-content">
  <iframe src="https://player.vimeo.com/video/1054046699?title=0&amp;byline=0&amp;portrait=0&amp;badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479" width="1080" height="1620" frameborder="0" allow="autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media" title="long - dynamic content"></iframe>
  </div>
</div>

<a href="#modal1" class="modal-button">modal 1</a>

<a href="#modal2" class="modal-button">modal 2</a>

Upvotes: 1

Views: 56

Answers (2)

ManuelMB
ManuelMB

Reputation: 1375

'use strict'

// Get the button that opens the modal
var btn = document.querySelectorAll("a.modal-button");

// All page modals
var modals = document.querySelectorAll(".modal");

// Get the <span> element that closes the modal
var spans = document.querySelectorAll(".close");


// When the user clicks the button, open the modal
//let iframe = document.querySelector('iframe');
//let player = new Vimeo.Player(iframe);

let iframes = document.querySelectorAll('iframe');
let iframesArray = Array.from(iframes)
 
let players = iframesArray.map(iframe => new Vimeo.Player(iframe)) 

//for (var i = 0; i < btn.length; i++) {
for (let i = 0; i < btn.length; i++) {  // Use `let` instead of `var`
  btn[i].onclick = function (e) {
    e.preventDefault();
    const modal = document.querySelector(e.target.getAttribute("href"));
    modal.style.display = "block";
        //player.play();
        players[i].play();
  };
}

// When the user clicks on <span> (x), close the modal
for (var i = 0; i < spans.length; i++) {
  spans[i].onclick = function () {
    for (var index in modals) {
      if (typeof modals[index].style !== "undefined")
        modals[index].style.display = "none";
        //player.pause();       
        if(index!== "undefined" && typeof index === 'number'){
          players[index].pause();   
        }
    }
  };
}

// When the user clicks anywhere outside of the modal, close it
window.onclick = function (event) {
  if (event.target.classList.contains("modal")) {
    for (var index in modals) {
      if (typeof modals[index].style !== "undefined")
        modals[index].style.display = "none";
        //player.pause();
        players[index].pause();
    }
  }
};

Upvotes: -1

zer00ne
zer00ne

Reputation: 44088

SO Snippets block <iframe>s so of course the example does not function properly. In order to review a functioning example you can copy and paste it into a text file and change the extension from .txt to .html. A copy of the example is also provided at CodePen as well.

A HTMLDialogElement is used instead of a <div> because the "close" event is used as one of the methods to control the Vimeo player. It's also semantically appropriate and simple to use. The example does a lot more than what is actually needed so I will only address the code that should resolve your problem.

  • By default the Vimeo player has a autopause feature enabled. autopause will allow only one player to play at a time.

  • When closing a modal the player continues to play. In the example below the player is paused when the "close" event is fired on the <dialog>. Since your modal has no special events to hook into, try pausing the player when the user clicks outside of the modal or clicks the close button. Do the following:

    /**
     * First collect all player objects into an array.
     * Which you've already done which is great.
     */
    let players = iframesArray.map(iframe => new Vimeo.Player(iframe))
    
    /**
     * Next, define a function that'll iterate through the 
     * players array and pause each one.
     */
    const pauseAll = () => {
      players.forEach((p) => {
        p.pause()
         .then()
         .catch()
      });
    };
    
    /**
     * Finally, add the function to the code block responsible 
     * for the close button and to the code block that closes
     * the modal when the user clicks outside of it.
     */
     for (var i = 0; i < spans.length; i++) {
       spans[i].onclick = function () {
         pauseAll()
         ....
    

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Vimeo Playlist Modal</title>
  <meta name="description" content="155 chars">
  <style>
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }

    :root {
      font: 500 5vmin/1.5 "Segoe UI";
    }

    body {
      overflow: scroll;
    }

    dialog {
      padding: 0;
      border: 0;
      border-radius: 5px;
      background: transparent;
      box-shadow: 0 10px 6px -6px #777;
      -ms-overflow-style: none;
      scrollbar-width: none;
    }

    dialog::backdrop {
      background: rgba(50, 50, 50, 0.3);
    }

    dialog::-webkit-scrollbar {
      display: none;
    }

    #ui {
      padding: 0;
      border: 1.5px solid #bbb;
      border-radius: 5px;
      background: #eee;
    }

    .btn {
      display: inline-flex;
      justify-content: center;
      align-items: center;
      padding: 0;
      border: 1px ridge #ddd;
      border-radius: 5px;
      font: inherit;
      font-size: 2rem;
      line-height: normal;
      background: transparent;
      cursor: pointer;
      box-shadow: 0 6px 4px -4px #bbb;
    }

    .btn:hover {
      color: #0a87a1;
      box-shadow: 0 6px 8px -4px #999;
    }

    .btn:active {
      color: #0a87a1;
      transform: scale(0.95);
    }

    .content {
      display: flex;
      flex-flow: column nowrap;
      justify-content: center;
      align-items: center;
      width: 100vh;
      padding: 0 0.5rem;
      border: 0;
      background: #eee;
    }

    .content legend {
      width: 100%;
    }

    .content legend .btn {
      float: right;
      height: 1.5rem;
      margin: 0.25rem -0.25rem 0.25rem 0;
      padding-bottom: 0.45rem;
      line-height: 0;
      color: #888;
    }

    .control {
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0 0 0.25rem;
      padding: 0;
      border: 0;
    }

    #prev,
    #next {
      width: 2rem;
      height: 2rem;
      padding: 0;
      border: 0;
      line-height: 1;
      background: #eee;
    }

    #counter {
      padding: 0.75rem 1rem 0;
      font-size: 1.25rem;
      font-family: Consolas;
      color: #0a87a1;
    }

    #video {
      width: 100%;
      margin: 0;
      padding: 0;
      border: 0;
    }

    .playlist a {
      position: relative;
      display: list-item;
      width: max-content;
      color: #18272f;
      text-decoration: none;
    }

    .playlist a+a {
      margin-top: 0.5rem;
    }

    .playlist a::before {
      content: "";
      position: absolute;
      bottom: -0.25rem;
      left: 0;
      width: 100%;
      height: 0.1rem;
      border-radius: 4px;
      background: #0a87a1;
      transform-origin: right;
      transform: scaleX(0);
      transition: transform 0.3s ease-in-out;
    }

    .playlist a::marker {
      content: "\0000bb\002009";
      display: inline-block;
      font-size: 1.4rem;
      color: #0a87a1;
    }

    .playlist a:hover::before {
      color: #0a87a1;
      transform-origin: left;
      transform: scaleX(1);
    }

    .playlist a b {
      font-weight: 600;
      font-size: 1.1rem;
      font-variant: small-caps;
    }

    .hidden {
      display: none;
    }
  </style>
</head>

<body>

  <button id="start">Double Click</button>

  <dialog>
    <form id="ui" method="dialog">
      <fieldset class="content">
        <legend>
          <input class="btn" type="submit" value="⨯">
        </legend>
        <fieldset id="video"></fieldset>
        <fieldset class="control">
          <input id="prev" class="btn" type="button" value="⏮">
          <output id="counter"></output>
          <input id="next" class="btn" type="button" value="⏭">
        </fieldset>
      </fieldset>
    </form>
  </dialog>

  <menu class="playlist"></menu>

  <script src="https://player.vimeo.com/api/player.js"></script>
  <script>
    let idx = 0;
    let players = [],
      links = [],
      media,
      iframes;

    const vIDs = ["148551759", "64142541", "108977978"];

    const ui = document.forms.ui;
    const io = ui.elements;
    const prev = io.prev;
    const next = io.next;
    const count = io.counter;
    const vid = io.video;

    const modal = document.querySelector("dialog");
    const list = document.querySelector(".playlist");

    const setVideos = (vIDs) => {
      vIDs.forEach((id) => {
        const obj = document.createElement("object");
        obj.name = "media";
        obj.className = "hidden";
        obj.dataset.vimeoId = id;
        obj.dataset.vimeoWidth = "720";
        vid.append(obj);
        players.push(new Vimeo.Player(obj));
      });
    };

    const smallCaps = (str) => {
      if (str.includes(" - ")) {
        return str.split(/(\s-\s)/)
          .map((w, i) => {
            return i === 0 ? `<b>${w}</b>` : w;
          })
          .join("");
      } else {
        return str.split(" ")
          .map((w) => {
            return /[A-Z]{2,}/.test(w) ?
              `<b>${w[0] + w.slice(1).toLowerCase()}</b>` : w;
          })
          .join(" ");
      }
    };

    const getTitles = () => {
      iframes = [...document.querySelectorAll("iframe")];
      media = Array.from(io.media);
      return iframes.map((f) => smallCaps(f.title));
    };

    const setLinks = (titles) => {
      titles.forEach((t) => {
        const link = document.createElement("a");
        link.href = "#";
        list.append(link);
        link.insertAdjacentHTML("beforeend", t);
        links.push(link);
      });
    };

    const pauseAll = () => {
      players.forEach((p) => p.pause()
        .then()
        .catch());
    };

    const switchMedia = (idx) => {
      media.forEach((m) => m.classList.add("hidden"));
      pauseAll();
      media[idx].classList.remove("hidden");
      count.value = idx + 1;
    };

    const clickList = (e) => {
      const clk = e.target;
      if (clk.matches("a")) {
        idx = links.indexOf(clk);
        switchMedia(idx);
        modal.showModal();
      }
    };

    list.onclick = clickList;

    const reverse = (e) => {
      idx--;
      idx = idx < 0 ? iframes.length - 1 : idx;
      switchMedia(idx);
    };

    const forward = (e) => {
      idx++;
      idx = idx > iframes.length - 1 ? 0 : idx;
      switchMedia(idx);
    };

    prev.onclick = reverse;
    next.onclick = forward;

    modal.onclick = (e) => e.currentTarget.close();
    ui.onclick = (e) => e.stopPropagation();

    modal.onclose = (e) => pauseAll();


    document.getElementById("start").onclick = init;

    function init(e) {
      setVideos(vIDs);
      setLinks(getTitles());
      setTimeout(() => this.className = "hidden", 3000);
    }
  </script>
</body>

</html>

Upvotes: 0

Related Questions