I am trying to lazy load my html video. It's in my WordPress template and I want for video to load when user get to its viewpoint.
I am new in JS so I think something is wrong. How can I test if lazy loading is working correctly?
Maybe my function is not valid?
<video id="myVideo" autoplay loop muted playsinline src="<?= VIDEO ?>/video_footer.mp4">
<source data-src="<?= VIDEO ?>/video_footer.mp4" type="video/mp4">
</video>
js:
$(function() {
$("video.myVideo source").each(function() {
var sourceFile = $(this).attr('data-src');
$(this).attr("src", sourceFile);
var video = this.parentElement;
video.load();
video.play();
});
});
It's unlikely that is working as it is.
You have already set the source to video so even if the function is working, the video file is already there, so no lazy load...
This is the correct setup:
<video id="myVideo" autoplay loop muted playsinline src="">
<source data-src="<?= VIDEO ?>/video_footer.mp4" type="video/mp4">
</video>
Then you can check if you function works
Another solution is to use intersection observer and do it with pure JavaScript, simple, lightweight and works in most cases
Lazy load video with pure JavaScript:
<script type="text/javascript">
const lazyvideo = document.querySelectorAll('.lazy-video');
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
console.log('video in the view');
if (entry.target.querySelector('source').getAttribute('data-src') !== null) {
const source = entry.target.querySelector('source').getAttribute('data-src')
entry.target.setAttribute('src', source);
}
observer.unobserve(entry.target);
} else {
console.log('video out of view');
}
});
});
lazyvideo.forEach(video => {
observer.observe(video);
});
</script>
I have added console.log('video out of view'); & console.log('video in the view'); in order to check the console whether the video is in view or not.
With this script if you want a video to be lazy loaded, just add a class lazy-video to it and you are all set ;)
Full code:
const lazyvideo = document.querySelectorAll('.lazy-video');
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
console.log('video in the view');
if (entry.target.querySelector('source').getAttribute('data-src') !== null) {
const source = entry.target.querySelector('source').getAttribute('data-src')
entry.target.setAttribute('src', source);
}
observer.unobserve(entry.target);
} else {
console.log('video out of view');
}
});
});
lazyvideo.forEach(video => {
observer.observe(video);
});
#myVideo {
max-width:400px;
width:100%;
}
<video id="myVideo" class="lazy-video" autoplay loop muted playsinline src="">
<source data-src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4">
</video>
If you want to be sure that lazy load works
Open you Chrome developer tools and go to Network and select the
media tab
While you have the tools open reload the page
If you haven't scroll to the video section then you should not see
the video in your console
While you scroll when you are in the view of the video section then
you must see the video loading on the media tab
See screenshots below:
Not there yet:
In view:
Related
I have a video gallery, each item looks like
<video muted="" allowfullscreen="" autoplay="" controls="" controlslist="nodownload noplaybackrate" disablepictureinpicture="" class="story loop="" poster="" style="max-height: 500px; width: 349px;" id="">
<source src="https://example.com/video.mp4" type="video/mp4">
</video>
They have to look like gifs - on autoplay, and if user switches on the sound on one of the video, the others have to became muted, because now it's possible to switch on the sound on more then one video, and it's too much sound.
I tried already that ways, and they didn't help
1
jQuery('video').on('volumechange', function() {
jQuery('video').attr('muted');
jQuery(this).removeAttr('muted');
})
2
jQuery('video').on('volumechange', function() {
jQuery('video').prop('muted', true);
jQuery(this).prop('muted', false);
})
3 - with the Class
jQuery('video').on('volumechange', function() { jQuery('video').removeClass('sound'); jQuery(this).addClass('sound'); jQuery('video').attr('muted'); jQuery('video.sound').removeAttr('muted');
})
and 4 - the native js after clothing jquery tag
const videoElements = document.getElementsByTagName('video');
for (const currentVideoElement of videoElements) {
currentVideoElement.addEventListener('volumechange', () => {
for (const otherVideoElement of videoElements) {
if (otherVideoElement !== currentVideoElement) {
otherVideoElement.muted = true;
}
}
});
}
What else could help, where I'm wrong?
Would be very good if no extra libraries needed.
The site itseldf
I'm use a tag html5 video + hls.js for video streaming .m3u8
<div class="container-video">
<video id="video"
width="700"
height="400"
preload="auto"
controls>
<source [src]="videoLink" type="application/x-mpegURL">
</video>
</div>
playVideoLive(videoLink) {
const video = document.getElementsByTagName('video')[0];
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoLink);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
video.play();
});
}
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoLink;
video.addEventListener('canplay', function () {
video.play();
});
}
}
Hoe i can show the dropdown with the list of quality video?
You can use an extra package to add the a track selector - there may be others but this one seems quite popular: https://www.npmjs.com/package/videojs-hls-quality-selector
You can also add your own controls and do it via the API using: https://github.com/video-dev/hls.js/blob/master/docs/API.md#hlscurrentlevel
There is a demo which uses the API here (at the time of writing) - go to the bottom of the demo page to see the levels and you can click on them there: https://hls-js-dev.netlify.app/demo/
I am adding multiple HTML5 videos onto a webpage.
The code I am replicating is from this recommended accessible approach. http://jspro.brothercake.com/audio-descriptions/ The video plays fine, and audio captions work, but when I add a new video to the same page the second video does not play the audio captions at all. Does anyone have suggestions on how I can fix this issue?
<video id="video" preload="auto" controls="controls"
width="640" height="360" poster="./media/HorribleHistories.jpg">
<source src="./media/HorribleHistories.mp4" type="video/mp4" />
<source src="./media/HorribleHistories.webm" type="video/webm" />
</video>
<audio id="audio" preload="auto">
<source src="./media/HorribleHistories.mp3" type="audio/mp3" />
<source src="./media/HorribleHistories.ogg" type="audio/ogg" />
</audio>
<script type="text/javascript">
(function()
{
//get references to the video and audio elements
var video = document.getElementById('video');
var audio = document.getElementById('audio');
//if media controllers are supported,
//create a controller instance for the video and audio
if(typeof(window.MediaController) === 'function')
{
var controller = new MediaController();
audio.controller = controller;
video.controller = controller;
}
//else create a null controller reference for comparison
else
{
controller = null;
}
//reduce the video volume slightly to emphasise the audio
audio.volume = 1;
video.volume = 0.8;
//when the video plays
video.addEventListener('play', function()
{
//if we have audio but no controller
//and the audio is paused, play that too
if(!controller && audio.paused)
{
audio.play();
}
}, false);
//when the video pauses
video.addEventListener('pause', function()
{
//if we have audio but no controller
//and the audio isn't paused, pause that too
if(!controller && !audio.paused)
{
audio.pause();
}
}, false);
//when the video ends
video.addEventListener('ended', function()
{
//if we have a controller, pause that
if(controller)
{
controller.pause();
}
//otherwise pause the video and audio separately
else
{
video.pause();
audio.pause();
}
}, false);
//when the video time is updated
video.addEventListener('timeupdate', function()
{
//if we have audio but no controller,
//and the audio has sufficiently loaded
if(!controller && audio.readyState >= 4)
{
//if the audio and video times are different,
//update the audio time to keep it in sync
if(Math.ceil(audio.currentTime) != Math.ceil(video.currentTime))
{
audio.currentTime = video.currentTime;
}
}
}, false);
})();
</script>
So your problem is to do with how you are grabbing the elements in the first place.
var video = document.getElementById('video');
var audio = document.getElementById('audio');
What you are doing is grabbing a single item on the page with the ID of "video" (same for "audio").
IDs have to be unique, so what you want to do is use classes instead.
<video class="video" preload="auto" controls="controls"
width="640" height="360" poster="./media/HorribleHistories.jpg">
See I changed the ID to a class.
Now any element with the class "video" can be used in our code.
However we do need to modify our code a bit as now we have multiple items to bind to.
please note the below is to give you an idea of how you loop items etc. You would need to rewrite your code to move each of the steps into functions etc. as your original code is not designed to work with multiple items
(function()
{
//get references to every single video and audio element
var videos = document.querySelectorAll('.video');
var audios = document.querySelectorAll('.audio');
// loop through all videos adding logic etc.
for(x = 0; x < videos.length; x++){
// grab a single video from our list to make our code neater
var video = videos[x];
if(typeof(window.MediaController) === 'function')
{
var controller = new MediaController();
video.controller = controller;
} else {
controller = null;
}
video.volume = 0.8;
//...etc.
}
})();
Quick Tip:
I would wrap your <video> and <audio> elements that are related in a <div> with a class (e.g. class="video-audio-wrapper").
This way you can change your CSS selector to something like:
var videoContainers = document.querySelectorAll('.video-audio-wrapper');
Then loop through them instead and check if they have a video and / or audio element
for(x = 0; x < videoContainers.length; x++){
var thisVideoContainer = videoContainers[x];
//query this container only - we can use `querySelector` as there should only be one video per container and that returns a single item / the first item it finds.
var video = thisVideoContainer.querySelector('video');
var audio = thisVideoContainer.querySelector('audio');
//now we can check if an element exists
if(video.length == 1){
//apply video logic
}
if(audio.length == 1){
//apply audio logic
}
// alternatively we can check both exist if we have to have both
if(video.length != 1 || audio.length != 1){
// we either have one or both missing.
// apply any logic for when a video / audio element is missing
//using "return" we can exit the function early, meaning all code after this point is not run.
return false;
}
///The beauty of this approach is you could then just use your original code!
}
Doing it this way you could recycle most of your code.
Thank you for your suggestions in changing the ID's into classes and adding the video wrapper <div> to the video container. That all makes sense in grouping each video on 1 page. I updated the the following code, but the audio captions won't play at all. The video plays and pauses fine, and the volume works. I am also not getting any syntax errors in the browser console. Here's what I got for my HTML and JS. I appreciate your help/feedback.
<div class="video-container-wrapper">
<div class="video-container">
<video class="video" preload="auto" controls="controls" width="640" height="360" poster="img/red-zone-thumb.png">
<source src="https://player.vimeo.com/external/395077086.hd.mp4?s=1514637c1ac308a950fafc00ad46c0a113c6e8be&profile_id=175" type="video/mp4">
<track kind="captions" label="English captions" src="captions/redzone-script.vtt" srclang="en" default="">
</video>
<audio class="audio" preload="auto">
<source src="captions/redzone-message.mp3" type="audio/mp3">
</audio>
</div>
</div>
Javascript:
var videoContainers = document.querySelectorAll('.video-container-wrapper');
for (x = 0; x < videoContainers.length; x++) {
var thisVideoContainer = videoContainers[x];
//query this container only - we can use `querySelector` as there should only be one video per container and that returns a single item / the first item it finds.
var video = thisVideoContainer.querySelector('video');
var audio = thisVideoContainer.querySelector('audio');
//now we can check if an element exists
if (video.length == 1) {
//apply video logic
//reduce the video volume slightly to emphasise the audio
video.volume = 0.8;
//when the video ends
video.addEventListener('ended', function () {
video.pause();
}, false);
}
if (audio.length == 1) {
//apply audio logic
audio.volume = 1;
//when the video plays
video.addEventListener('play', function () {
if (audio.paused) {
audio.play();
}
}, false);
// when the video ends
video.addEventListener('ended', function () {
audio.pause();
}, false);
//when the video time is updated
video.addEventListener('timeupdate', function () {
if (audio.readyState >= 4) {
//if the audio and video times are different,
//update the audio time to keep it in sync
if (Math.ceil(audio.currentTime) != Math.ceil(video.currentTime)) {
audio.currentTime = video.currentTime;
}
}
}, false);
}
}
I'm trying to make a random video player in html/js.
It is supposed to play a different video on pageload and as soon as one video is over, it should play another one.
HTML
<video width="320" height="240" autoplay>
<source src="abcdefg.com/example1.mp4" type="video/mp4">
<source src="abcdefg.com/example2.mp4" type="video/mp4">
</video>
JS
<script>
var videos = [
[{type:'mp4', 'src':'abcdefg.com/example1.mp4'}],
[{type:'mp4', 'src':'abcdefg.com/example2.mp4'}],
];
$(function() {
var number = Math.floor(Math.random()*videos.length);
$(this).find('source').each(function(index){
videoSrc = videos[number][index].src;
$(this).attr('src', videoSrc);
$('video').load();
$('video').play();
});
});
</script>
However, my current code plays the same video every page reload and as soon as it's over, nothing happens.
How do I need to optimize my code so it automatically plays a different video on every pageload + when the previous video is over?
Try this,I have added an event listener to video end property and called the newvideo() function ,so that every time the video finishes a new random video is loaded from array.I could'nt find more video urls ,you can test and let me know if it works for you.
$(document).ready(function(){
var videos = [
[{type:'mp4', 'src':'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'}],
[{type:'mp4', 'src':'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'}],
[{type:'mp4', 'src':'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'}]
];
// selecting random item from array,you can make your own
var randomitem = videos[Math.floor(Math.random()*videos.length)];
// This function adds a new video source (dynamic) in the video html tag
function videoadd(element, src, type) {
var source = document.createElement('source');
source.src = src;
source.type = type;
element.appendChild(source);
}
// this function fires the video for particular video tag
function newvideo(src)
{
var vid = document.getElementById("myVideo");
videoadd(vid,src ,'video/ogg');
vid.autoplay = true;
vid.load();
//vid.play();
}
// function call
newvideo(randomitem[0].src)
// Added an event listener so that everytime the video finishes ,a new video is loaded from array
document.getElementById('myVideo').addEventListener('ended',handler,false);
function handler(e)
{
newvideo(randomitem[0].src)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<video width="320" height="240" autoplay id="myVideo">
</video>
I use the following code to check if the user is on desktop or mobile, if on desktop the src="" attribute of the video sources is populated. All fine. After populating the src attribute, I want to check the video has loaded before displaying it. Is there a way to do this?
Thanks!
JS
//video
if($.browser.mobile)
{
console.log("is mobile")
// it is mobile browser
}
else
{
console.log("is desktop")
// no mobile browser
var sources = document.querySelectorAll('video#patient-video source');
// Define the video object this source is contained inside
var video = document.querySelector('video#patient-video');
for(var i = 0; i<sources.length;i++) {
sources[i].setAttribute('src', sources[i].getAttribute('data-src'));
}
// If for some reason we do want to load the video after, for desktop as opposed to mobile (I'd imagine), use videojs API to load
video.load();
video.muted= "muted";
$(".main-area--cris-pro").addClass("loaded")
}
To check if it's a mobile browser, I use the plugin:
detectmobilebrowser.js
My HTML is as follows:
<video id="patient-video" width="100%" preload="none" poster="../../assets/img/patient-home-1600.jpg" autoplay loop>
<source data-src="../../assets/video/patient-home-video-comp.mp4" src="" type="video/mp4">
<source data-src="../../assets/video/patient-home-video-comp.webm" src="" type="video/webm">
<source data-src="../../assets/video/patient-home-video-comp.ogv" src="" type="video/ogg">
</video>
Use canplaythrough event.
The canplaythrough event is fired when the user agent can play the media, and estimates that enough data has been loaded to play the media up to its end without having to stop for further buffering of content.
var sources = document.querySelectorAll('video#patient-video source');
var video = document.querySelector('video#patient-video');
for (var i = 0; i < sources.length; i++) {
sources[i].setAttribute('src', sources[i].getAttribute('data-src'));
}
video.muted = true;//Set boolean value
video.addEventListener('canplaythrough', function() {
alert('Video Loaded!');
video.muted = false;
$(".main-area--cris-pro").addClass("loaded");
});