Spotify Demo

68 of 124

Easily implement custom functionality like media embeds and custom events within your grids.

Result Full HTML CSS JS
Edit Download

Spotify Charts Daily Global Viral 50

Currently Playing

No. [[index.position]]

Album Art
Currently Playing
[[record.trackName]]

Full Code

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>ZingGrid: Blank Grid</title>
  <script nonce="undefined" src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
  <style>
    /* Namespaced Body */
    .zg-body {
      background: #000;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      font-family: 'Rubik', sans-serif;
    }

    /* Shared Defaults */
    zg-caption,
    zg-head,
    zg-body {
      background: #000;
    }

    /* ZingGrid Element */
    zing-grid {
      max-width: 900px;
      margin: 0 auto;
      border: 0;
      background: #000;
      font-family: 'Rubik', sans-serif;
      color: #ffffff;
      opacity: 1;
      --theme-color-primary: #1ed65f;
      --zg-select-arrow-color: #1ed65f;
      --zg-control-bar-background: #000;
      --zg-control-bar-border-bottom: #4b4a4a;
      --zg-head-background: #000;
      --zg-option-list-color: #000;
    }

    zing-grid.loading {
      opacity: 0;
      transition: opacity .3s ease-out;
    }

    zing-grid[loading],
    zing-grid[loading] zg-body {
      height: 700px;
    }

    /* Special styling when in 'row' mode */
    zing-grid[layout="row"] {
      min-width: 700px;
    }

    /* Caption */
    zg-caption h2 {
      color: #fff;
      font-weight: bold;
      font-size: 37px;
      margin: 0 0 -50px 0;
      border-bottom: 0px solid #333030;
    }

    zg-caption a {
      font-size: 17px;
      font-weight: normal;
      margin-left: 20px;
      color: #1ed65f;
      text-decoration: none;
    }

    zg-caption a:hover {
      color: #15a548;
    }

    zg-header[layout="card"] zg-caption {
      height: 5rem;
    }

    zg-header[layout="card"] zg-caption a {
      display: block;
      margin-left: 0;
    }

    /* Grid Head */
    zg-head {
      border-bottom: 0px solid #4b4a4a;
      margin-bottom: 15px;
    }

    /* Grid Controls */
    zg-control-bar {
      background: #000;
    }

    /* Grid Rows */
    zg-row[layout="card"] {
      background: #181717;
      margin: 5px;
      border-radius: 5px;
      -webkit-border-radius: 5px;
      -moz-border-radius: 5px;
      border-left: 1px solid #282626;
      border-top: 1px solid #282626;
      border-right: 1px solid #191818;
      border-bottom: 1px solid #191818;
    }

    zg-head zg-row[layout="card"] {
      display: none;
    }

    zg-head zg-row[layout="card"] zg-head-cell {
      display: none;
    }

    zg-body[layout="row"] zg-row {
      border-top: 1px solid #4b4a4a;
      border-bottom: 1px solid #4b4a4a;
    }

    /* Grid Cells */
    .zg-head-cell-inner {
      border-bottom: 1px solid #4b4a4a;
      color: #1ed65f;
    }

    zg-cell:first-child {
      margin-top: -25px;
      font-weight: bold;
      color: #1ed65f;
      font-size: 12px;
    }

    zg-row[layout="row"] zg-cell:first-child p {
      white-space: nowrap;
    }

    zg-cell:nth-child(2) {
      margin-top: -30px;
    }

    zg-cell:nth-child(2) img {
      border-radius: 5px;
      -moz-border-radius: 5px;
      -webkit-border-radius: 5px;
      border-left: 1px solid #191818;
      border-top: 1px solid #191818;
      border-right: 1px solid #353232;
      border-bottom: 1px solid #353232;
      margin: 0 auto;
    }

    zg-row[layout="row"] zg-cell:nth-child(2) img {
      width: 100px;
      border: 0;
      border-radius: 0;
      -webkit-border-radius: 0;
      -moz-border-radius: 0;
      margin-bottom: -3px;
    }

    zg-cell:nth-child(3) {
      margin-top: -25px;
      font-size: 25px;
      font-weight: bold;
    }

    zg-row[layout="row"] zg-cell:nth-child(3) {
      margin-top: -25px;
      font-size: 20px;
    }

    zg-cell:nth-child(4) {
      font-size: 15px;
      margin-top: -30px;
    }

    zg-cell:last-child {
      padding-bottom: 10px;
    }

    zg-row[layout="card"] video {
      width: 100%;
      margin-top: -80px;
    }

    zg-row[layout="row"] video {
      margin-top: -80px;
    }

    /* Album Covers */
    .image--container {
      position: relative;
    }

    .image--container img {
      width: 100%;
    }

    .image--container [data-type] {
      position: absolute;
      opacity: 0;
      top: calc(50% - 20px);
      left: calc(50% - 20px);
    }

    /* Currently Playing */
    .currently-playing {
      position: absolute;
      margin-top: -20px;
      font-size: 11px;
      color: #62D26D;
      opacity: 0;
    }

    /* Styling for Media 'State' Controls */
    zg-body zg-row:hover .image--container [data-type="play-button"],
    zg-body zg-row[playing]:hover .image--container [data-type="pause-button"],
    zg-body zg-row[layout="row"][playing] [data-field-index="trackName"] .currently-playing,
    zg-body zg-row[layout="card"][playing] [data-field-index="position"] .currently-playing {
      opacity: 1;
    }

    zg-body zg-row[playing]:hover .image--container [data-type="play-button"] {
      opacity: 0;
    }

    zg-body zg-row[layout="card"][playing] [data-field-index="position"] .currently-playing {
      right: 17px;
      margin-top: 11px;
    }

    /* Row/card Border Styling */
    zg-body zg-row {
      border: 2px solid transparent;
    }

    zg-body zg-row:hover {
      border: 2px solid #62D26D;
      background: rgba(22, 62, 26, 0.5);
    }

    zg-body zg-row[playing] {
      background: rgba(62, 62, 62, 0.5);
    }

    zg-body zg-row[layout="card"][playing] {
      border: 2px solid #62D26D;
    }

    /* Pager */
    zg-pager {
      border-top: 0;
      padding-top: 10px;
    }

    /* MEDIA QUERIES (MAX WIDTH)
---------------------------------------- */

    /* BELOW TABLET: PORTRAIT */
    @media (max-width: 766px) {
      zing-grid[layout="card"] {
        max-width: 400px;
      }
    }

    zing-grid[loading] {
      height: 698px;
    }
  </style>
</head>

<body class="zg-body">

  <link href="https://fonts.googleapis.com/css?family=Rubik:400,700" rel="stylesheet">

  <zing-grid src="https://zinggrid-examples.firebaseio.com/spotify-demo" layout-controls loading pager page-size="6" page-size-options="6,12,24,50" class="loading">
    <zg-caption>
      <h2>Spotify Charts <a href="https://spotifycharts.com/viral/global/daily/latest" target="_blank" crossorigin>Daily Global Viral 50</a></h2>
    </zg-caption>
    <zg-colgroup>
      <zg-column index="position" type="number" header=" ">
        <span class="currently-playing">
          <b>Currently Playing</b>
          <svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M10 0.454846V8.47925C10 8.47925 9.96875 9.85379 8.254 9.85379C6.3765 9.85379 6.25 8.73669 6.25 8.47833C6.25 7.92586 6.4855 7.32364 8.25 7.32364C9.06225 7.32364 9.25 7.13222 9.25 6.64439C9.25 6.64439 9.25 3.09962 9.25 2.7977C9.25 2.49579 9.23 2.38667 8.86425 2.46255C8.235 2.59299 4.125 3.41001 3.961 3.446C3.797 3.48199 3.75 3.57117 3.75 3.83617C3.75 4.32377 3.75 9.10806 3.75 9.62455C3.75 10.141 3.3145 11 1.75 11C0.1855 11 0 10.0835 0 9.62455C0 8.94301 0.5625 8.50332 1.75175 8.50332C2.8595 8.50332 3 8.217 3 7.79061C3 6.27004 3 1.82113 3 1.60106C3 1.38099 3.09775 1.23221 3.3415 1.18292C3.74925 1.10085 9.5365 0.00667707 9.5365 0.00667707C9.5365 0.00667707 10 -0.0902925 10 0.454846Z" fill="#62D26D" />
          </svg>
        </span>
        <p>No. [[index.position]]</p>
      </zg-column>
      <zg-column index="image" type="image" header=" " width=100>
        <div class="image--container">
          <img src="[[index.image]]" alt="Album Art">
          <div class="video-action--container">
            <span data-type="play-button">
              <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path d="M20 0C8.95 0 0 8.95 0 20C0 31.05 8.95 40 20 40C31.05 40 40 31.05 40 20C40 8.95 31.05 0 20 0ZM16 29V11L28 20L16 29Z" fill="white" />
              </svg>
            </span>
            <span data-type="pause-button">
              <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path fill-rule="evenodd" clip-rule="evenodd" d="M20 0C9 0 0 9 0 20C0 31 9 40 20 40C31 40 40 31 40 20C40 9 31 0 20 0ZM18 28H14V12H18V28ZM26 28H22V12H26V28Z" fill="white" />
              </svg>
            </span>
          </div>
        </div>
      </zg-column>
      <zg-column index="trackName" header=" ">
        <span class="currently-playing">
          <b>Currently Playing</b>
          <svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M10 0.454846V8.47925C10 8.47925 9.96875 9.85379 8.254 9.85379C6.3765 9.85379 6.25 8.73669 6.25 8.47833C6.25 7.92586 6.4855 7.32364 8.25 7.32364C9.06225 7.32364 9.25 7.13222 9.25 6.64439C9.25 6.64439 9.25 3.09962 9.25 2.7977C9.25 2.49579 9.23 2.38667 8.86425 2.46255C8.235 2.59299 4.125 3.41001 3.961 3.446C3.797 3.48199 3.75 3.57117 3.75 3.83617C3.75 4.32377 3.75 9.10806 3.75 9.62455C3.75 10.141 3.3145 11 1.75 11C0.1855 11 0 10.0835 0 9.62455C0 8.94301 0.5625 8.50332 1.75175 8.50332C2.8595 8.50332 3 8.217 3 7.79061C3 6.27004 3 1.82113 3 1.60106C3 1.38099 3.09775 1.23221 3.3415 1.18292C3.74925 1.10085 9.5365 0.00667707 9.5365 0.00667707C9.5365 0.00667707 10 -0.0902925 10 0.454846Z" fill="#62D26D" />
          </svg>
        </span>
        <div>[[record.trackName]]</div>
      </zg-column>
      <zg-column index="artist" header=" "></zg-column>
      <zg-column index="url" renderer="video" header=" " hidden>
        <template>
          <video controls name="media">
            <source src="[[index.url]]" type="audio/mpeg">
          </video>
        </template>
      </zg-column>
    </zg-colgroup>
  </zing-grid>
  <script>
    ZingGrid.setLicense(['26ccbfec16b8be9ee98c7d57bee6e498']); // EVENTS
    // ---------------------------
    // window:load event for Javascript to run after HTML
    // because this Javascript is injected into the document head
    window.addEventListener('load', () => {
      // Javascript code to execute after DOM content
      const zgRef = document.querySelector('zing-grid');
      // handle playing the songs
      zgRef.addEventListener('row:click', _handleRowClick);
      zgRef.addEventListener('card:click', _handleRowClick);
      // handle changing layouts to keep playing track
    });
    // Custom loading class for CSS handling
    const zgLoaded = document.querySelector('zing-grid');
    zgLoaded.addEventListener('grid:ready', () => {
      setTimeout(() => zgLoaded.classList.remove('loading'), 0);
    });
    // HELPER FNS
    // ---------------------------
    // Event handler when media has stopped (to remove 'playing' state)
    function handleMediaEnded(rowRef) {
      rowRef.removeAttribute('playing');
    }
    // Render the video markup
    function video(index, cellDOMRef, cellRef) {
      const videoRef = cellDOMRef.querySelector('video');
      const rowRef = cellDOMRef.parentNode;
      const mediaEventHandler = handleMediaEnded.bind(null, rowRef);
      // Attach event listeners for when the media ends
      videoRef.addEventListener('ended', mediaEventHandler);
      // Remove active state if row already playing (caused by layout switch)
      if (rowRef.hasAttribute('playing'))
        rowRef.removeAttribute('playing');
    }
    // Handle row/card click to start/stop a video
    function _handleRowClick(e) {
      const rowDetails = e.detail.ZGTarget;
      const videoRef = rowDetails.querySelector('video');
      const allRows = document.querySelectorAll('zg-body zg-row');
      // pause all other videos 
      [...allRows].forEach(row => {
        const video = row.querySelector('video');
        // skip currently playing video
        if (videoRef == video)
          return;
        // pause all other videos
        // and remove active state
        if (!video.paused) {
          video.pause();
          row.removeAttribute('playing');
        }
      });
      // if we are playing
      if (videoRef) {
        if (videoRef.paused) {
          videoRef.play();
          rowDetails.setAttribute('playing', 'true');
        } else {
          videoRef.pause();
          rowDetails.removeAttribute('playing');
        }
      }
    }
  </script>
</body>

</html>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>ZingGrid: Blank Grid</title>
    <script src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
  </head>
  <body class="zg-body">
    
    <link href="https://fonts.googleapis.com/css?family=Rubik:400,700" rel="stylesheet">
    
    <zing-grid 
      src="https://zinggrid-examples.firebaseio.com/spotify-demo"
      layout-controls
      loading
      pager
      page-size="6"
      page-size-options="6,12,24,50"
      class="loading"
    >
      <zg-caption>
        <h2>Spotify Charts <a href="https://spotifycharts.com/viral/global/daily/latest" target="_blank" crossorigin>Daily Global Viral 50</a></h2>
      </zg-caption>
    	<zg-colgroup>
        <zg-column index="position" type="number" header=" ">
          <span class="currently-playing">
            <b>Currently Playing</b>
            <svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
            	<path d="M10 0.454846V8.47925C10 8.47925 9.96875 9.85379 8.254 9.85379C6.3765 9.85379 6.25 8.73669 6.25 8.47833C6.25 7.92586 6.4855 7.32364 8.25 7.32364C9.06225 7.32364 9.25 7.13222 9.25 6.64439C9.25 6.64439 9.25 3.09962 9.25 2.7977C9.25 2.49579 9.23 2.38667 8.86425 2.46255C8.235 2.59299 4.125 3.41001 3.961 3.446C3.797 3.48199 3.75 3.57117 3.75 3.83617C3.75 4.32377 3.75 9.10806 3.75 9.62455C3.75 10.141 3.3145 11 1.75 11C0.1855 11 0 10.0835 0 9.62455C0 8.94301 0.5625 8.50332 1.75175 8.50332C2.8595 8.50332 3 8.217 3 7.79061C3 6.27004 3 1.82113 3 1.60106C3 1.38099 3.09775 1.23221 3.3415 1.18292C3.74925 1.10085 9.5365 0.00667707 9.5365 0.00667707C9.5365 0.00667707 10 -0.0902925 10 0.454846Z" fill="#62D26D"/>
            </svg>
          </span>
          <p>No. [[index.position]]</p>
        </zg-column>
        <zg-column index="image" type="image" header=" " width=100>
          <div class="image--container">
						<img src="[[index.image]]" alt="Album Art">
            <div class="video-action--container">
              <span data-type="play-button">
                <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                  <path d="M20 0C8.95 0 0 8.95 0 20C0 31.05 8.95 40 20 40C31.05 40 40 31.05 40 20C40 8.95 31.05 0 20 0ZM16 29V11L28 20L16 29Z" fill="white"/>
                  </svg>
              </span>
              <span data-type="pause-button">
                <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                  <path fill-rule="evenodd" clip-rule="evenodd" d="M20 0C9 0 0 9 0 20C0 31 9 40 20 40C31 40 40 31 40 20C40 9 31 0 20 0ZM18 28H14V12H18V28ZM26 28H22V12H26V28Z" fill="white"/>
                </svg>
              </span>
            </div>
          </div>
        </zg-column>
        <zg-column index="trackName" header=" ">
        	<span class="currently-playing">
            <b>Currently Playing</b>
            <svg width="10" height="11" viewBox="0 0 10 11" fill="none" xmlns="http://www.w3.org/2000/svg">
            	<path d="M10 0.454846V8.47925C10 8.47925 9.96875 9.85379 8.254 9.85379C6.3765 9.85379 6.25 8.73669 6.25 8.47833C6.25 7.92586 6.4855 7.32364 8.25 7.32364C9.06225 7.32364 9.25 7.13222 9.25 6.64439C9.25 6.64439 9.25 3.09962 9.25 2.7977C9.25 2.49579 9.23 2.38667 8.86425 2.46255C8.235 2.59299 4.125 3.41001 3.961 3.446C3.797 3.48199 3.75 3.57117 3.75 3.83617C3.75 4.32377 3.75 9.10806 3.75 9.62455C3.75 10.141 3.3145 11 1.75 11C0.1855 11 0 10.0835 0 9.62455C0 8.94301 0.5625 8.50332 1.75175 8.50332C2.8595 8.50332 3 8.217 3 7.79061C3 6.27004 3 1.82113 3 1.60106C3 1.38099 3.09775 1.23221 3.3415 1.18292C3.74925 1.10085 9.5365 0.00667707 9.5365 0.00667707C9.5365 0.00667707 10 -0.0902925 10 0.454846Z" fill="#62D26D"/>
            </svg>
          </span>
          <div>[[record.trackName]]</div>
        </zg-column>
        <zg-column index="artist" header=" "></zg-column>
        <zg-column index="url" renderer="video" header=" " hidden>
          <template>
            <video controls name="media">
              <source src="[[index.url]]" type="audio/mpeg">
            </video>
          </template>
        </zg-column>
      </zg-colgroup>
    </zing-grid>
  </body>
</html>
/* Namespaced Body */
.zg-body {
  background:#000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: 'Rubik', sans-serif;
}

/* Shared Defaults */
zg-caption,
zg-head,
zg-body {
  background: #000;
}

/* ZingGrid Element */
zing-grid {
  max-width: 900px;
	margin: 0 auto;
  border: 0;
  background: #000;
  font-family: 'Rubik', sans-serif;
  color: #ffffff;
  opacity: 1;
  --theme-color-primary: #1ed65f;
  --zg-select-arrow-color: #1ed65f;
  --zg-control-bar-background: #000;
  --zg-control-bar-border-bottom: #4b4a4a;
  --zg-head-background: #000;
  --zg-option-list-color: #000;
}
zing-grid.loading { opacity:0; transition:opacity .3s ease-out; }
zing-grid[loading],
zing-grid[loading] zg-body { height:700px; } 
/* Special styling when in 'row' mode */
zing-grid[layout="row"] { min-width:700px; }

/* Caption */
zg-caption h2 {
  color: #fff;
  font-weight: bold;
  font-size: 37px;
  margin: 0 0 -50px 0;
  border-bottom: 0px solid #333030;
}
zg-caption a {
  font-size: 17px;
  font-weight: normal;
  margin-left: 20px;
  color: #1ed65f;
  text-decoration: none;
}
zg-caption a:hover {
  color: #15a548;
}
zg-header[layout="card"] zg-caption {
  height: 5rem;
}
zg-header[layout="card"] zg-caption a {
  display: block;
  margin-left: 0;
}

/* Grid Head */
zg-head {
  border-bottom: 0px solid #4b4a4a;
  margin-bottom: 15px;
}

/* Grid Controls */
zg-control-bar {
  background: #000;
}

/* Grid Rows */
zg-row[layout="card"] {
  background: #181717;
  margin: 5px;
  border-radius: 5px;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  border-left: 1px solid #282626;
  border-top: 1px solid #282626;
  border-right: 1px solid #191818;
  border-bottom: 1px solid #191818;
}
zg-head zg-row[layout="card"] {
  display: none;
}
zg-head zg-row[layout="card"] zg-head-cell {
  display: none;
}

zg-body[layout="row"] zg-row {
	border-top: 1px solid #4b4a4a;
	border-bottom: 1px solid #4b4a4a;
}

/* Grid Cells */
.zg-head-cell-inner {
	border-bottom: 1px solid #4b4a4a;
  color: #1ed65f;
}

zg-cell:first-child {
  margin-top: -25px;
  font-weight: bold;
  color: #1ed65f;
  font-size: 12px;
}

zg-row[layout="row"] zg-cell:first-child p {
  white-space: nowrap;
}

zg-cell:nth-child(2) {
  margin-top: -30px;
}

zg-cell:nth-child(2) img {
  border-radius: 5px;
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-left: 1px solid #191818;
  border-top: 1px solid #191818;
  border-right: 1px solid #353232;
  border-bottom: 1px solid #353232;
  margin: 0 auto;
}

zg-row[layout="row"] zg-cell:nth-child(2) img {
  width: 100px;
  border: 0;
  border-radius: 0;
  -webkit-border-radius: 0;
  -moz-border-radius: 0;
  margin-bottom: -3px;
}

zg-cell:nth-child(3) {
  margin-top: -25px;
  font-size: 25px;
  font-weight: bold;
}

zg-row[layout="row"] zg-cell:nth-child(3) {
  margin-top: -25px;
  font-size: 20px;
}

zg-cell:nth-child(4) {
  font-size: 15px;
  margin-top: -30px;
}

zg-cell:last-child {
  padding-bottom: 10px;
}

zg-row[layout="card"] video {
  width: 100%;
  margin-top: -80px;
}

 zg-row[layout="row"] video {
  margin-top: -80px;
}

/* Album Covers */
.image--container { position:relative; }
.image--container img { width:100%; }
.image--container [data-type] { 
  position:absolute;
  opacity:0;
  top: calc(50% - 20px);
  left: calc(50% - 20px);
}

/* Currently Playing */
.currently-playing {
  position:absolute;
  margin-top: -20px;
  font-size: 11px;
  color: #62D26D;
	opacity: 0;
}

/* Styling for Media 'State' Controls */
zg-body zg-row:hover .image--container [data-type="play-button"],
zg-body zg-row[playing]:hover .image--container [data-type="pause-button"],
zg-body zg-row[layout="row"][playing] [data-field-index="trackName"] .currently-playing,
zg-body zg-row[layout="card"][playing] [data-field-index="position"] .currently-playing { opacity:1; }
zg-body zg-row[playing]:hover .image--container [data-type="play-button"] { opacity:0; }
zg-body zg-row[layout="card"][playing] [data-field-index="position"] .currently-playing { right:17px; margin-top:11px; }

/* Row/card Border Styling */
zg-body zg-row { border:2px solid transparent; }
zg-body zg-row:hover {
  border:2px solid #62D26D;
  background: rgba(22, 62, 26, 0.5);
}
zg-body zg-row[playing] {
  background: rgba(62, 62, 62, 0.5);
}
zg-body zg-row[layout="card"][playing] {
  border:2px solid #62D26D;
}

/* Pager */
zg-pager {
  border-top: 0;
  padding-top: 10px;
}

/* MEDIA QUERIES (MAX WIDTH)
---------------------------------------- */

/* BELOW TABLET: PORTRAIT */
@media (max-width: 766px) {
  zing-grid[layout="card"] {
      max-width: 400px;
  }
}
// EVENTS
// ---------------------------
// window:load event for Javascript to run after HTML
// because this Javascript is injected into the document head
window.addEventListener('load', () => {
  // Javascript code to execute after DOM content
  const zgRef = document.querySelector('zing-grid');
  // handle playing the songs
  zgRef.addEventListener('row:click', _handleRowClick);
  zgRef.addEventListener('card:click', _handleRowClick);
  // handle changing layouts to keep playing track
});
// Custom loading class for CSS handling
const zgLoaded = document.querySelector('zing-grid');
zgLoaded.addEventListener('grid:ready', () => {
  setTimeout(() => zgLoaded.classList.remove('loading'), 0);
});
// HELPER FNS
// ---------------------------
// Event handler when media has stopped (to remove 'playing' state)
function handleMediaEnded(rowRef) {
  rowRef.removeAttribute('playing');
}
// Render the video markup
function video(index, cellDOMRef, cellRef) {
  const videoRef = cellDOMRef.querySelector('video');
  const rowRef = cellDOMRef.parentNode;
  const mediaEventHandler = handleMediaEnded.bind(null, rowRef);
  // Attach event listeners for when the media ends
  videoRef.addEventListener('ended', mediaEventHandler);
  // Remove active state if row already playing (caused by layout switch)
  if (rowRef.hasAttribute('playing'))
    rowRef.removeAttribute('playing');
}
// Handle row/card click to start/stop a video
function _handleRowClick(e) {
  const rowDetails = e.detail.ZGTarget;
  const videoRef = rowDetails.querySelector('video');
  const allRows = document.querySelectorAll('zg-body zg-row');
  // pause all other videos 
  [...allRows].forEach(row => {
    const video = row.querySelector('video');
    // skip currently playing video
    if (videoRef == video)
      return;
    // pause all other videos
    // and remove active state
    if (!video.paused) {
      video.pause();
      row.removeAttribute('playing');
    }
  });
  // if we are playing
  if (videoRef) {
    if (videoRef.paused) {
      videoRef.play();
      rowDetails.setAttribute('playing', 'true');
    } else {
      videoRef.pause();
      rowDetails.removeAttribute('playing');
    }
  }
}

Interested in this demo? Modify it to your needs in ZingSoft Studio, our testing sandbox. It's free to sign up, and you can come back and edit at any time!

Edit in Studio

Demo Gallery