'"clone" audio player to control another?

I have a list of players that user scrolls through and I'm trying create an always-visible player control, so user don't have to scroll through the list looking for the player that is playing sound.

I'd like somehow "clone" <audio> element to make it not actually play anything, just show information about currently playing media in another player and visually be identical to original player.

Currently I generate my own "player" and use media events to gather/display information about playing player, but it probably looks different in different browsers/systems.

Any suggestions?

const content = document.getElementById("content");

const eventHandler = (e =>
{
  const remote = document.getElementById("remote"), //our custom "player"
        progress = remote.querySelector(".progress"),
        time = remote.querySelector(".time"),
        position = time.querySelector(".position"),
        duration = time.querySelector(".duration");
        ;

  let player = null; //this will hold current player
  //list of event handlers
  const handler = {
    play: e =>
    {
      if (player && player !== e.target)
        player.pause();

      player = e.target;
      remote.classList.add("player");
      remote.classList.add("play");
      content.querySelector(".selected")?.classList.remove("selected");
      player.classList.add("selected");
      handler.timeupdate(e);
      handler.durationchange(e);
    },

    pause: e =>
    {
      if (player.paused)
        remote.classList.remove("play");
    },

    timeupdate: e =>
    {
      if (player !== e.target)
        return;

      position.textContent = readableTime(e.target.currentTime);
      progress.value = e.target.currentTime;
    },

    durationchange: e =>
    {
      if (player !== e.target)
        return;

      duration.textContent = readableTime(e.target.duration);
      progress.max = e.target.duration;
    }
  }

  const readableTime = (t, ms) =>
  {
    const seconds = ~~t,
          minutes = ~~(seconds / 60);

    return ("" + minutes).padStart(1, "0") + ":" +
           ("" + seconds % 60).padStart(2, "0") +
           (ms ? "." + (t.toFixed(1)+".0").substr((""+t).indexOf(".")+1, 1) : "");
  }

  remote.querySelector(".playpause").addEventListener("click", e =>
  {
    player[player.paused ? "play" : "pause"]();
  });

  progress.addEventListener("input", e =>
  {
    player.currentTime = progress.value;
  });

  return e =>
  {
    try{handler[e.type](e)}catch(er){console.error(er)};
  };
})();


document.addEventListener("play", eventHandler, true);
document.addEventListener("pause", eventHandler, true);
document.addEventListener("timeupdate", eventHandler, true);


/* show players list */
{
  const files = ["Zh[Wci", "ceei[", "iY_\\_", "ZWdY[", "^eki[", "fefZWdY["];
  let file,
      player = document.createElement("audio");

  player.setAttribute("controls", "");
  player.setAttribute("nodownload", "");
  while((file = files.splice(~~(Math.random() * files.length), 1)).length)
  {
  /* https://www.bensound.com/ */
    player.src = `^jjfi0%%mmm$X[diekdZ$Yec%X[diekdZ#cki_Y%X[diekdZ#${file[0]}$cf)`.replace(/./g,a=>String.fromCharCode(a.charCodeAt(0)+10) ) ;
    content.appendChild(player);
    player = player.cloneNode(true);
  }
}
.content
{
  display: flex;
  margin: auto;
}
#content
{
  display: grid;
  max-height: 7em;
  overflow-y: auto;
  margin-top: 1em;
}

#remote
{
  font-family: Roboto-Regular, Roboto, sans-serif;
  font-size: 14px;
  display: flex;
  user-select: none;
  cursor: default;
  border: 1px solid black;
  background-color: white;
  color: black;
  border-radius: 2em;
  width: fit-content;
  height: 2em;
  padding: 0.5em 1.3em;
  align-items: center;
  gap: 10px;
  max-height: 2em;
}

#remote:not(.player)
{
  pointer-events: none;
  opacity: 0.5;
}

/* play/pause button */
#remote .playpause
{
  height: 1em;
  width: 1em;
  padding: 0.5em;
  margin: auto -0.3em;
  border-radius: 100%;
  text-align: center;
  cursor: pointer;
}

/* play */
#remote .playpause:after
{
  --size: 1em;
  width: var(--size);
  height: var(--size);
  content: "";
  display: inline-block;
  box-sizing: border-box;
  border-style: solid;
  border-width: calc(var(--size) / 2) 0 calc(var(--size) / 2) var(--size);
  border-color: transparent transparent transparent black;
  transition: all 100ms ease-in-out;
}

/* pause */
#remote.play .playpause:after
{
  border-style: double;
  border-width: 0 0 0 var(--size);
}

#remote .playpause:hover
{
  background-color: rgba(32, 33, 36, 0.06);
}

#remote .progress
{
  cursor: pointer;
}

#remote span
{
}

audio
{
  border: 2px solid transparent;
  border-radius: 2em;
  height: 2.5em;
}

audio.selected
{
  outline: 0;
  border-color: orange;
}
<div id="remote">
  <span class="playpause"></span>
  <span class="time">
    <span class="position">0:00</span>
    /
    <span class="duration">0:00</span>
  </span>
  <input type="range" class="progress" min="0" value="0">
</div>

<div class="content">
  <div id="content">
  </div>
</div>


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source