Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Doesn't recognize media when elements are created dynamically in DOM #940

Open
Patronics opened this issue Jul 8, 2024 · 5 comments
Open

Comments

@Patronics
Copy link

Patronics commented Jul 8, 2024

if either the media-control-bar or media-controller are created dynamically with JS, such as with the code

var trackNumber = 1
const controlBarHTML = `<media-control-bar mediacontroller='mediaControllerForTrack${trackNumber}'>
              <media-play-button></media-play-button>
              <media-mute-button></media-mute-button>
              <media-time-display showduration></media-time-display>
              <media-volume-range></media-volume-range>
              <media-time-range></media-time-range>
              <media-pip-button></media-pip-button>
              <media-fullscreen-button></media-fullscreen-button>
            </media-control-bar>`*/
          document.getElementById("media-container").InsertAdjacentHTML('beforeend',controlBarHTML)

Both the media-controller and the media-control-bar are created, but they're unable to connect to each other.
This results in very cumbersome use when handling multiple tracks that need to be independently controlled. The only workaround I've found so far is prepopulating the HTML with a bunch of identical elements in a hidden div, where each has a unique ID, and they are moved into place with the Javascript.

Also worth noting, the <audio> tag I'm applying the media controller to is in a shadow-dom, so that may be a factor in this issue. Regardless, my workaround is functional, although impractical to scale

@Patronics Patronics changed the title Doesn Doesn't recognize media when elements are created dynamically in DOM Jul 8, 2024
@Patronics
Copy link
Author

If the issue is related to the shadow dom, this pull request's commit might help resolve it?

@heff
Copy link
Collaborator

heff commented Jul 9, 2024

In the example code you have, it's using media-container, which is like media-controller but without the state management, and would result in the the problem you're describing. Is that the issue?

Otherwise, any chance you can make a code sandbox/code pen that demonstrates this?

@Patronics
Copy link
Author

In the example code you have, it's using media-container, which is like media-controller but without the state management, and would result in the the problem you're describing. Is that the issue?

thanks for the suggestion, but in this case media-container was actually just a generic name for the container I was inserting elements into, as a generic example (in my actual code it has a different name, "waveform-container"). I didn't include the media-controller code in the example because it works when the media-control-bar is not created programmatically but instead preexisting in the html source, and the method of inserting it with my media is somewhat convoluted (the media element itself is inside an open shadow-dom from another project).

I can try to look into creating a more minimal demonstration template.

@heff
Copy link
Collaborator

heff commented Jul 11, 2024

Ok cool, that'd be really helpful. This could be in the depths of the element association logic so will be hard to debug without an example.

@Patronics
Copy link
Author

Patronics commented Jul 12, 2024

Okay, here's a pair of example source code files that demonstrate the issue. The layout of elements produced is identical, the only difference is how they're created, and whether the end-result works. The setup assumes a file named 'demoSong.mp3' in the same directory as the HTML source file.

Working version:

<html>
<body>
<script type="module" src="https://cdn.jsdelivr.net/npm/media-chrome@3/+esm"></script>
<media-controller id='mediaController' audio></media-controller>
<div id="waveform">
  <!-- the waveform will be rendered here -->
</div>
<div id="new-container">
<media-control-bar mediacontroller='mediaController'>
    <media-play-button></media-play-button>
    <media-mute-button></media-mute-button>
    <media-time-display showduration></media-time-display>
    <media-volume-range></media-volume-range>
    <media-time-range></media-time-range>
    <media-pip-button></media-pip-button>
    <media-fullscreen-button></media-fullscreen-button>
  </media-control-bar>
</div>

<script type="module">


import WaveSurfer from 'https://cdn.jsdelivr.net/npm/wavesurfer.js@7/dist/wavesurfer.esm.js'

const wavesurfer = WaveSurfer.create({
  container: '#waveform',
  url: './demoSong.mp3',
  mediaControls: false,
})
//this section is important, this allows media-chrome to recognize the wavesurfer instance, by attaching to the <audio> tag
var mediaShadowRoot=document.getElementById('waveform').getElementsByTagName('div')[0].shadowRoot;
var shadowAudioNode = mediaShadowRoot.childNodes[5]
shadowAudioNode.setAttribute('slot','media');
mediaShadowRoot.appendChild(document.getElementById('mediaController'))
mediaShadowRoot.getElementById('mediaController').appendChild(shadowAudioNode)


wavesurfer.on('interaction', () => {
  wavesurfer.play()
})
</script>
</body>

</html>

Broken Version:

<html>
<body>
<script type="module" src="https://cdn.jsdelivr.net/npm/media-chrome@3/+esm"></script>
<media-controller id='mediaController' audio></media-controller>
<div id="waveform">
  <!-- the waveform will be rendered here -->
</div>
<div id="new-container">

</div>

<script type="module">


import WaveSurfer from 'https://cdn.jsdelivr.net/npm/wavesurfer.js@7/dist/wavesurfer.esm.js'

const wavesurfer = WaveSurfer.create({
  container: '#waveform',
  url: './demoSong.mp3',
  mediaControls: false,
})
//this section is important, this allows media-chrome to recognize the wavesurfer instance, by attaching to the <audio> tag
var mediaShadowRoot=document.getElementById('waveform').getElementsByTagName('div')[0].shadowRoot;
var shadowAudioNode = mediaShadowRoot.childNodes[5]
shadowAudioNode.setAttribute('slot','media');
mediaShadowRoot.appendChild(document.getElementById('mediaController'))
mediaShadowRoot.getElementById('mediaController').appendChild(shadowAudioNode)
//this section should work like in the previous example, but doesn't
const controlBarHTML = `<media-control-bar mediacontroller='mediaController'>
    <media-play-button></media-play-button>
    <media-mute-button></media-mute-button>
    <media-time-display showduration></media-time-display>
    <media-volume-range></media-volume-range>
    <media-time-range></media-time-range>
    <media-pip-button></media-pip-button>
    <media-fullscreen-button></media-fullscreen-button>
  </media-control-bar>`
document.getElementById("new-container").insertAdjacentHTML('beforeend',controlBarHTML)


wavesurfer.on('interaction', () => {
  wavesurfer.play()
})
</script>
</body>

</html>

Also worth noting that the same problematic behavior is observed if the media-controller element is created such as with insertAdjacentHTML or the following construction example rather than moved into place with AppendChild, as it is in the above examples.

var mediaController = document.createElement("media-controller");
         mediaController.id = `mediaController`
         mediaController.setAttribute("audio","")
         mediaShadowRoot.appendChild(mediaController)
         mediaShadowRoot.getElementById(`mediaController`).appendChild(shadowAudioNode)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants