Skip to content

Dynamically manipulating <source> src attribute *should* yield an effect #3588

Open
@benwiley4000

Description

@benwiley4000

tl;dr

Due to confusion about the nature of my request I want to clarify I am not suggesting I should be able to recycle the source list concept in HTML as a sequential playlist. I am merely suggesting I should be able to go from this:

<audio>
  <source src="song1.ogg">
  <source src="song1.mp3">
</audio>

to (by mutating the src attributes):

<audio>
  <source src="song2.ogg">
  <source src="song3.mp3">
</audio>

And the currentSrc should update to use one of the new src attributes on the <source> children.

jsfiddle showing current behavior

original posting

I was reading the <source> element spec and came across the following while trying to troubleshoot a bug in my library:

Dynamically modifying a source element and its attribute when the element is already inserted in a video or audio element will have no effect. To change what is playing, just use the src attribute on the media element directly, possibly making use of the canPlayType() method to pick from amongst available resources. Generally, manipulating source elements manually after the document has been parsed is an unnecessarily complicated approach.

Respectfully - I disagree! My component allows a user to specify a sequential playlist of songs to cycle through, and a list of potential sources for each song in the playlist, to be used by the browser as the browser sees fit.

The above quotation implies I am only able to benefit from the browser's automatic source selection for the first song I load. I have to take a totally imperative approach for subsequent tracks, which is a shame in a context like React where I would like to be able to declaratively specify my source list as a function of the currently chosen song index.

What reads simpler to you:

render() {
  const index = this.state.activeIndex;
  const sources = this.props.sourcesByIndex[index];
  return (
    <audio ref={ref => this.audio = ref}>
      {sources.map(source =>
        <source key={source.src} src={source.src} type={source.type} />
      )}
    </audio>
  );
}

or:

componentDidUpdate(prevProps, prevState) {
  const newIndex = this.state.activeIndex;
  if (newIndex !== prevState.activeIndex) {
    for (const source of this.props.sourcesByIndex[newIndex]) {
      const mimeType =
        source.type ||
        useSomeDatabaseLookupLibraryToFindMimeType(source.src);
      if (this.audio.canPlayType(mimeType) {
        this.audio.src = source.src;
        break;
      }
    }
  }
}

render() {
  // same render implementation as above
}

Manually iterating through my source options, checking canPlayType() for each, and updating the src attribute seems to me an unnecessarily complicated approach! 😄

Sidenote

While reading through both the spec and MDN I came across a notably discrepancy.

Spec

(Emphasis mine)

If the type attribute is not specified, the user agent will not select a different source element if it finds that it does not support the image format after fetching it.

The quotation refers specifically to images, but I'm assuming it applies to audio and video as well.

MDN

(Emphasis mine again)

If the type attribute isn't specified, the media's type is retrieved from the server and checked to see if the user agent can handle it; if it can't be rendered, the next <source> is checked.

Which is it - implementation-wise? If the MDN version turns out to be what's really in the wild, then we're missing a critical feature - automatic mimetype retrieval - when forced to restort to the method of source updating suggested in the spec.

ETA

In my original code example I mapped this.props.sources in the render method - I meant to just map sources. This is updated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    clarificationStandard could be clearerimpacts documentationUsed by documentation communities, such as MDN, to track changes that impact documentationtopic: media

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions