I want to update the source tag in a HTML5 video element so that when I click a button, whatever's playing switches to a new video.
I have a Clip component that returns an HTML5 video element, with the source URL provided via props.
function Clip(props) {
return (
<video width="320" height="240" controls autoPlay>
<source src={props.url} />
</video>
)
}
I also have a Movie component, which contains multiple URLs, each corresponding to a section of the movie. It also contains a position attribute, which acts like an iterator by remembering which section is currently playing.
class Movie extends Component {
constructor() {
super();
this.state = {
sections: [
'https://my-bucket.s3.amazonaws.com/vid1.mp4',
'https://my-bucket.s3.amazonaws.com/vid2.mp4'
],
position: 0
};
}
render() {
const sections = this.state.sections
const position = this.state.position
return (
<div>
{position}
<Clip url={sections[position]} />
<button onClick={() => this.updatePosition()}>Next</button>
</div>
)
}
updatePosition() {
const position = this.state.position + 1;
this.setState({ position: position });
}
}
The Movie component renders a Clip component and a "Next" button. When the Next button gets clicked, I want to update the position attribute and re-render the HTML5 video element using the next URL from sections.
As of now, when I hit Next the HTML5 video source tag updates, but the element continues playing the video from the previous URL. Can anyone help me figure out how to reset the video element?
Update: I created a JSFiddle, which you can see here.
Update 2: Updating the src attribute on the video element works!
Quick and dirty awnser: Add a key prop to <Clip> or <video>, e.g.:
function Clip({ url }) {
return (
<video key={url}>
<source src={url} />
</video>
);
}
Recommended awnser: Trigger a load event for the video element whenever there's a new URL, e.g.:
function Clip({ url }) {
const videoRef = useRef();
useEffect(() => {
videoRef.current?.load();
}, [url]);
return (
<video ref={videoRef}>
<source src={url} />
</video>
);
}
Explanation: The video initially doesn't change because in essence you're only modifying the <source> element and React understands that <video> should remain unchanged, so it does not update it on the DOM and doesn't trigger a new load event for that source. A new load event should be triggered for <video>.
The dirty answer works because when you change the key of a React element, React understands that's a whole new element. It then creates a new element in the DOM which naturally will have its own load event triggered on mount.
Changing the src of <source /> doesn't seem to switch the video for some reason. This isn't a react issue I don't think. Probably just how <source /> works. Maybe you have to call .load() on the element again. But it's just easier to set it directly on the element itself.
See the comment to the top answer: Can I use javascript to dynamically change a video's source?
You can set src directly on element instead:
function Clip(props) {
return (
<video width="320" height="240" src={props.url} controls autoPlay>
</video>
)
}
Bind the URL in the src property of Video tag and not Source Tag
<video autoplay loop muted playsinline="true" webkit-playsinline="true" [src]="videoSrc" type="video/mp4">
Add a parent div to second video
My code:
{isMobile ? (
<video
autoPlay={true}
loop
className="moviles"
muted
playsInline
poster="/totto/kids.png"
>
<source src="/totto/losmoviles.webm"></source>
<source src="/totto/losmoviles.mp4"></source>
</video>
) : (
<div>
<video
autoPlay={true}
loop
className="moviles2"
muted
playsInline
poster="/totto/portada.jpg"
>
<source src="/totto/moviles.webm"></source>
<source src="/totto/moviles.mp4"></source>
</video>
</div>
)}
You can use the key attribute :
<video key={videoUrl} autoPlay width="100%">
<source src={videoUrl} type="video/mp4"></source>
</video>
If the video's key attribute change, the video component will be re-mounted
Related
I have a modal component with a video in it:
<div>
<video controls #videoElement>
<source src="assets/videos/video.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
<div>
#ViewChild('videoElement') videoElement: ElementRef<HTMLVideoElement>;
ngOnDestroy(): void {
this.videoElement.nativeElement.pause();
}
Do I need to pause the video inside "ngOnDestroy"? please explain why.
Thank you!
I am making a web app with video playback and custom controls: a Next Video button and a Previous Video Button. My component looks like this:
const videos = [
'https://d3t1nqlebka5eu.cloudfront.net/videos3/82818a63-1972-4e39-b296-9cd3b85b0b39.mp4',
'https://d3t1nqlebka5eu.cloudfront.net/videos3/dd4dd6c5-95b7-41c2-b131-7b62392ad1b5.mp4',
'https://d3t1nqlebka5eu.cloudfront.net/videos3/29812506-e67d-4cab-b2b3-82461a4eadf1.mp4',
]
function App() {
const [videoIndex, setVideoIndex] = useState(0);
const h1Ref = useRef<HTMLVideoElement>(null);
return (
<div>
<DivButton onClick={() => {
setVideoIndex(getPrevIndex(videoIndex));
h1Ref.current!.load();
}}>
Prev
</DivButton>
{' '}
<DivButton onClick={() => {
setVideoIndex(getNextIndex(videoIndex));
h1Ref.current!.load();
}}>
Next
</DivButton>
<div>
<video id="video_player" autoPlay muted loop playsinline preload="" ref={h1Ref}>
<source id="video_player_source" src={videos[videoIndex]} type="video/mp4"/>
</video>
</div>
</div>
);
}
The problem is this: when I click Next or Prev buttons, the videos do switch between one another, but after every switch they are entering fullscreen mode on iOS. I don't need that, I need my controls around the video being played.
Is there a way to prevent that?
Assuming you already found an answer but for other people looking at this in the future, I suspect the issue was your capitalization of playsinline - in React you have to provide it as playsInline in order for it to be applied correctly, but that prop should prevent iOS from forcing videos into fullscreen when they start playing.
I have a styled component that styles a video tag.
const Video = styled.video`
...
`
And I use it like so
<Video width='200px' height='200px' autoplay='autoplay' muted loop>
<source src={img.src} type='video/mp4' />
</Video>
But the autoplay attribute doesn't work and also doesn't show up when I inspect the element with Devtools. The other attributes like width, height and loop are visible.
However when I use a normal video tag, the autoplay works and it is seen when I inspect the element.
<video width='200px' height='200px' autoplay='autoplay' muted loop>
<source src={img.src} type='video/mp4' />
</video>
Any idea why the styled-component does not recognise the autoplay attribute?
try autoPlay instead of autoplay
<Video ... autoPlay />
reference:
'autoplay' attribute
If you are using NPM then add the react player dependencies
npm install react-player --save
If you are using yarn add react player dependencies
yarn add react-player
import the ReactPlayer component in index file
import ReactPlayer from 'react-player';
Now you can set
<YourComponent url='https://www.youtube.com/linkhere' playing />
Bydefault it is False.
You can set it as True in props or inside the condition.
I had a similar issue and thanks to the answers above I could solve it.
The video element seemed working with either autoPlay="autoPlay" or autoPlay, however I had to reload the page manually in order to make it work.
<Video
autoplay="autoplay" // not working
autoPlay="autoPlay" // working
autoPlay // working
autoplay // not working
muted
loop
>
<source src={DroneShot} type="video/mp4" />
</Video>
I have an HTML 5 video inside my page and I would like to set the src dynamically.
I'm using vue, inside my js controller I set a variable with the video path then I use the variable in my page as below:
<video width="450" controls>
<source v-model="controller.var" v-bind:src="var" type="video/mp4">
</video>
The player doesn't load the video but my var is properly set with the right url.
What I'm missing here?
Thanks
Adding the key attribute with the source's URL to the video element will cause both video and source to be updated when the URL changes:
<video
:key="video.mp4"
width="450"
controls
>
<source
:src="video.mp4"
type="video/mp4"
>
</video>
This supports dynamic replacement of a video + source.
First, I don't know if you are actually using var in your template, but if you are, Vue will throw a warning in the template.
avoid using JavaScript keyword as property name: "var" in expression :src="var"
Second, you cannot dynamically change the source element.
From the HTML5 specification,
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.
So, bind your data to the src attribute of the video element.
<video width="450" controls :src="video"></video>
console.clear()
new Vue({
el:"#app",
data:{
video: "https://www.w3schools.com/tags/movie.mp4"
},
methods:{
changeVideo(){
this.video = "http://techslides.com/demos/sample-videos/small.mp4"
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<video width="450" controls :src="video"></video>
<div>
<button #click="changeVideo">Change</button>
</div>
</div>
If you need to change the src dynamically, you can change the src and load the new src with the .load() function.
new Vue({
el:"#app",
data:{
src: ""
},
methods:{
changeVideo(newSrc){
this.$data.src = newSrc;
//Force video load.
var vid = this.$refs.video;
vid.load();
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="app">
<video width="450" controls :src="src" ref="video"></video>
<div>
<button #click="changeVideo('http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4')">Change1</button>
<button #click="changeVideo('https://www.w3schools.com/tags/movie.mp4')">Change2</button>
</div>
</div>
Try setting the entire source as variable in the controller.
<video controls autoplay>
{{{ src }}}
</video>
new Vue({
el: '#app',
data: {
src: '<source src="#url" type="video/mp4">'
}
})
I need to set a listener for a video on SurveyGizmo that allows me to execute a function when the video ends. The HTML is as follows:
<div class="mejs-mediaelement">
<video height="534" width="800" src="urlOfTheVideo" type="video/mp4"></video>
</div>
so far I've tried to do this but it is not working:
<script>
function trailerEnd() {
document.getElementByClassName('mejs-mediaelement').addEventListener('ended',alertThem);
function alertThem(e) {
alert('video has ended');
};
};
trailerEnd();
If video is immediate child element of .megs-mediaelement div, then you could select video element and add event to it like this.
mediaelement.js adds an extra wrapper around video tag, so using immediate child selector(>) won't work. Updated answer with decnendent selector. It should work now.
var videoBlock = document.querySelector('.megs-mediaelement video');
videoBlock.addEventListener('ended', trailerEnd, false);
function trailerEnd(){
console.log('video ended');
}
<div class="megs-mediaelement">
<video width="320" height="240" controls>
<source src="https://www.w3schools.com/TAgs/movie.mp4" type="video/mp4">
<source src="https://www.w3schools.com/TAgs/movie.ogg" type="video/ogg">
</video>
</div>
Alternative you could also just select the video by using var videoBlock = document.querySelector('video'); if there only one video on the page.