Replaying a gif from start with createElement in React - javascript

I'm trying to create a function in my React project that will create a new element that plays one loop of a gif, then deletes the element. So far I have been semi-successful using this:
function playGif() {
var gif = document.createElement('img')
gif.className = "gif-play"
document.body.appendChild(gif)
setTimeout(() => { gif.parentNode.removeChild(gif); }, 600)
}
With the following CSS:
.gif-play {
width: 256px;
height: 256px;
position: absolute;
background-image: url(./assets/images/animations/atk1.gif);
background-size: cover;
}
However, no matter what I set the timeout to delete the element (even the precise duration of the gif in milliseconds) the gif will still start clipping and playing from the middle after several button clicks.
I have also tried setting the image src directly like this:
function playGif() {
var gif = document.createElement('img')
gif.src = require('./assets/images/animations/atk1.gif')
document.body.appendChild(gif)
setTimeout(() => { gif.parentNode.removeChild(gif); }, 600)
}
But with this the gif never shows up at all, just a broken image. I thought using require() in React was supposed to fix that, but it still doesn't work for me.
So any tips on how to get my gif to play from the beginning when I call the function would be great.
Thanks!

not sure exactly what you want but I have prepared one animation for you. You can check it. Hope it helps you.
PlayGifExample Component
import React, {useEffect} from "react";
import PlayGif from './PlayGif.css'
function PlayGifExample() {
function animate(i) {
if(i > 1000)
return;
let gif = document.createElement('img');
gif.className = "gif-play";
gif.style.cssText = `left: ${i}px`;
console.log(i);
document.body.appendChild(gif);
setTimeout(() => {
gif.parentNode.removeChild(gif);
animate(i+1);
}, 10)
}
function playGif() {
let gif = document.createElement('img');
gif.className = "gif-play";
gif.style.cssText = `left: 1px`;
document.body.appendChild(gif);
setTimeout(() => {
gif.parentNode.removeChild(gif);
animate(2);
}, 10)
}
return (
<>
<button onClick={playGif}>Click me</button>
</>
);
}
export default PlayGifExample;
PlayGif.css
.gif-play {
width: 256px;
height: 256px;
position: absolute;
background-image: url(./images/4.png);
background-size: cover;
}

Related

Matching setState interval with css animation

I am rotating the items in an array of images inside an interval of 5 seconds. Then I have a css animation with styled components that eases in the gallery images with a fade that occurs at the same time interval.
const fadeIn = keyframes`
5%, 95% { opacity: 1 }
100% { opacity: 0 }
`
export const Gallery = styled.div<{ lapse: number }>`
position: relative;
margin: 0 auto;
max-width: 1064px;
opacity: 0;
animation: ${fadeIn} ease-in-out ${({ lapse }) => `${lapse}s`} infinite;
`
The problem is that when I change the state even thou at first it seems in sync, eventually the setState takes a bit longer
import React, { useState, useEffect } from 'react'
const [images, setImages] = useState<string[]>([])
// Time in seconds for each image swap, fading in between
const lapse = 5
...
useEffect(() => {
// Clone the images array
const imgs = [...images]
// Time interval same as css animation to fade in
const interval = setInterval(() => {
// Take the first element and put it at the end
imgs.push(...imgs.splice(0, 1))
// Update the state, this seems to desync as time passes
setImages(imgs)
}, lapse * 1000)
return () => clearInterval(interval)
}, [images])
return (
<Gallery lapse={lapse}>
<Portal>
<img src={imgs/${images[0]}`}
</Portal>
<Thumbnails>
<Thumbwrapper>
<img src={imgs/${images[1]}`}
</Thumbwrapper>
<Thumbwrapper>
<img src={imgs/${images[2]}`}
</Thumbwrapper>
</Thumbnails>
</Gallery>
)
Is there a way I can make sure the swapping happends smoothly?
Make the component class based,
constructor(props) {
super(props);
this.state = {
interval: null,
images: ["1.jpg", "2.jpg", "3.jpg"],
timeChanged: null
};
}
// setInterval when component is mounted
componentDidMount() {
var interval = setInterval(()=> {
// use callback argument for setState
// when new value (images) depends on old value
this.setState((state)=>({
timeChanged: (new Date()).toString(),
images: state.images.slice(1).concat(state.images[0])
// add 1st img at the end
});
}, 3000);
this.setState({interval: interval});
}
// clearInterval before component is unmounted
componentWillUnmount() {
clearInterval(this.state.interval);
}
When the state is updated using its old value the callback argument should be used.
(See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous)
slice returns a portion of an array.
Also see how to update state arrays in React.
If you still wish to use hooks. Just call setImages with
(images)=>images.slice(1).concat(images[0]).
I'm not sure where the clearTimeout should be, when using hooks.
It would be great if we can have example in codesandbox. Also I would like to ask you, maybe I am missing something, but how do you determine when to stop using effect, since it depends on image, which is, I assume, part of the useSate hook, and in that same effect you are updating that same image, which will lead to another effect usage(leading to infinity loop)?
UPDATED ANSWER
I made similar example on codesandbox to look it on my own. It looks to me that main problem here is sync between applied CSS and actual update in your react component. When using setInterval with 5000ms it does not mean that it will be triggered right after 5000ms, but CSS animation on the other side is always right on time, so it stuck when two of those become unsynced.
It looks like the only way to solve this is to recreate img elements on each reorder, by introducing timestamp state and use it as part of the each img key in order to force img recreation on each update. Also you would need to change animation in simpler way.
CSS would besomething like this:
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.image {
position: relative;
margin: 0 auto;
max-width: 1064px;
animation: fadeIn 3s;
opacity: 1;
}
And functional component would be something like this(hook part only):
const [images, setImages] = useState(["1.jpg", "2.jpg", "3.jpg"]);
const [dateTimeWhenChanged, setDatetimeWhenChanged] = useState("");
useEffect(() => {
const imgs = [...images];
// Time interval same as css animation to fade in
const interval = setInterval(() => {
// Take the first element and but it to the end
imgs.push(...imgs.splice(0, 1));
// Update the state, this seems to desync as time passes
setImages(imgs);
setDatetimeWhenChanged(new Date().toString());
}, 5 * 1000);
return (
<div>
{images.map((img, ind) => (
<img
key={`${ind}-${dateTimeWhenChanged}`}
alt={img}
src={`images/${img}`}
className="imgg"
/>
))}
</div>
);
I came to the conclusion that having two 'clocks' to keep the time, one being react setState and the other css animation, was at the core of the problem.
So I am now keeping a single interval with a setTimeout to make sure it goes in order then substract the time taken on the timeouts from the interval and toggle the class for css
export const Gallery = styled.div<{ fadeIn: boolean }>`
position: relative;
margin: 0 auto;
max-width: 1064px;
transition: opacity 0.3s;
opacity: ${({ fadeIn }) => (fadeIn ? 1 : 0)};
`
import React, { useState, useEffect } from 'react'
const [images, setImages] = useState<string[]>([])
const [fadeIn, setFadeIn] = useState<boolean>(true)
useEffect(() => {
let interval: ReturnType<typeof setInterval>
if (images.length) {
interval = setInterval(() => {
setFadeIn(false)
setTimeout(() => {
setImages(images => images.slice(1).concat(images[0]) )
setFadeIn(true)
}, 400)
}, (lapse - 0.4) * 1000)
}
return () => clearInterval(interval)
}, [images])
const getImage = (i: number) => <img src={imgs/${images[i]}`} />
<Gallery fadeIn={fadeIn}>
<Portal>{getImage(0)}</Portal>
<Thumbnails>
<Thumbwrapper>{getImage(1)}</Thumbwrapper>
<Thumbwrapper>{getImage(2)}</Thumbwrapper>
</Thumbnails>
</Gallery>
I did however used #Nice Books better setState aproach

JS/CSS Loading spinner finishes before JS executes

Kinda JS/dev newbie here. Having a play around with a loading spinner - this is the example I'm working from. I've currently got a bunch of JS calculations that are performed on a Flask/SQLite backend API. I'll ignore the CSS as it's likely irrelevant.
document.onreadystatechange = function() {
if (document.readyState !== "complete") {
document.querySelector("main").style.visibility = "hidden";
document.querySelector("#loader").style.visibility = "visible";
} else {
document.querySelector("#loader").style.display = "none";
document.querySelector("main").style.visibility = "visible";
}
};
This in the html:
<main role="main" class="container">
<div id="loader" class="spinner-1"></div>
...content here...
</main>
<script type="text/javascript" src="{{ url_for ('static', filename='scripts/main.js') }}"></script>
The issue is that the JS is still running on DOM load. So the spinner disappears and items are still being added to the DOM via JS. They appear after the spinner disappears, which negates the point of having a spinner!
I've tried several of these methods, but they all work the same.
I thought about having the conditional tied to one of the loading items, but that seems a bit clunky and I'd not be able to repeat the code on other pages on the site. Is there a baked in JS method for doing this properly?
EDIT - some of the JS I'm using
async function recipeGet () {
let response = await fetch('/recipeget/' + recipeId)
let data = await response.json();
return data
};
recipeGet();
async function efficiency () {
let mEfficiency = (await recipeGet()).efficiency;
mEfficiency = mEfficiency / 100
return mEfficiency
}
async function insertEff () {
let eff = await efficiency();
let effElement = document.querySelector('#eff');
effElement.innerText = (((Math.round(abv * 100) / 100 )) + "%");
};
insertEff();
I appreciate this may not be the right way to do things, but I'm still relatively new to developing, and it works currently.
ANSWER:
With the suggestion of the answer below, I managed to implement this:
JS
async function insertEff () {
let eff = await efficiency();
let effElement = document.querySelector('#eff');
effElement.innerText = (((Math.round(abv * 100) / 100 )) + "%");
document.querySelector("#loader").style.display = "none";
document.querySelector("#spins").style.visibility = "visible";
};
HTML
<div id="loader" class="spinner-1"></div>
<div class="tab-content" id="spins">
CSS
#spins {
visibility: hidden;
}
Where spins is the ID of the division I want to hide. It is initially hidden, then unhides when the function is executed. I still have to toy around with the HTML as the spinner jigs around a bit on page load, but that's a relatively trivial problem.
I would maybe change how you're receiving the data on your front end. It seems like you're trying to directly add the data from the back end to the front end using some kind of template.
Instead of doing this, try doing a fetch request from the front end after the page is loaded. That way you get the page much faster, and your spinner will be coordinated with the data you receive, meaning it will only disappear once you have all the data.
Here's an example of an ajax/fetch request:
const API_URL = 'https://opentdb.com/api.php?amount=3'
const dataWrapper = document.querySelector('.data-wrapper')
const spinner = document.querySelector('.spinner')
const data = document.querySelector('.data')
const fetchData = async (URL) => {
try {
const res = await fetch(URL)
const json = await res.json()
// populate data with json
data.innerText = JSON.stringify(json, undefined, 2)
spinner.remove() // or just make display: none; - whatever you need
} catch(err) {
console.log(err.message)
}
}
fetchData(API_URL)
.data-wrapper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.spinner {
position: absolute;
width: 100px;
height: 100px;
font-size: 100px;
color: blue;
animation: spin 1s linear infinite;
display: flex;
justify-content: center;
align-items: center;
}
#keyframes spin {
to {
transform: rotate(360deg);
}
}
.data {
background: lightgray;
color: #235789;
font-family: 'Cascadia Code', monospace;
word-break: break-all;
white-space: pre-wrap;
width: 100%;
height: 100%;
overflow-y: scroll;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-iBBXm8fW90+nuLcSKlbmrPcLa0OT92xO1BIsZ+ywDWZCvqsWgccV3gFoRBv0z+8dLJgyAHIhR35VZc2oM/gI1w==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<div class="data-wrapper">
<div class="spinner">
<i class="fas fa-spinner"></i>
</div>
<pre class="data"></pre>
</div>
The point is, by doing an ajax call in this way, the line where the spinner gets removed will only be reached and executed once the data is fully received.
window.onload = function() {
setTimeout(() => {
document.querySelector('#loader').style.opacity = '0';
setTimeout(() => {
document.querySelector('#loader').style.display = 'none';
document.querySelector('main').style.visibility = 'visible';
}, 200);
}, 1000);
}
I use this code to make my preloader smooth and hide after 1 second of window onload. Maybe this code will help you.

Class is being removed with lazy loading JavaScript

I am trying to Lazy load some images onto a website. The problem that I am encountering is that I have to remove the class for the lazy loading to work but I still want the CSS properties to be used by the image. Can anyone help?
// JavaScript Document
document.addEventListener("DOMContentLoaded", function() {
var lazyImages = [].slice.call(document.querySelectorAll("img.Images"));
if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.srcset = lazyImage.dataset.srcset;
lazyImage.classList.remove("Images");
lazyImageObserver.unobserve(lazyImage);
}
});
});
lazyImages.forEach(function(lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Possibly fall back to a more compatible method here
}
});
.Images {
width: 190px;
height: 190px;
padding: 10px;
object-fit: cover;
}
<img class="Images" src="https://i.imgur.com/mf5YDAy.jpg" data-src="https://i.imgur.com/mf5YDAy.jpg" data-srcset="https://i.imgur.com/mf5YDAy.jpg, https://i.imgur.com/mf5YDAy.jpg" />

Preloading background images

I'm building a page that loops through 3 different backgrounds, changing every 750ms. To do this, I'm adding a class to the body with the relevent background image, changing with JS. For the first loop through, they flash because the image has to load, so it's not there instantly. Therefore is there any methods I can use to preload the images?
CSS:
&.backgrounds{
background-position: center bottom;
background-repeat: no-repeat;
background-size: 130%;
&.freddy{
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_freddy.jpg);
}
&.irene{
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_irene.jpg);
}
&.joe{
background-image: url(/img/illustrations/snapchat/snapchat_holding_page_joe.jpg);
}
}
JS:
setInterval(function() {
if ( $('.backgrounds').hasClass('freddy') ){
$('.backgrounds').removeClass('freddy').addClass('irene');
} else if ( $('.backgrounds').hasClass('irene') ){
$('.backgrounds').removeClass('irene').addClass('joe');
} else if ( $('.backgrounds').hasClass('joe') ){
$('.backgrounds').removeClass('joe').addClass('freddy');
}
}, 750);
I would do something like this. loadImages returns a Promise that will resolve once all of the images are loaded. The .then attached to it calls cycleImages, which starts up the interval. Since you will need the URLs in the JS anyway to do the pre-loading, instead of class switching I'm directly manipulating the background-image, that way you can remove the image URLs from the CSS and save a few redundant bytes. This also makes it easier to expand the list of images in the future too, you only need to add an item to the array of images instead of maintaining a complicated if statement.
function loadImages (images) {
// each image will be loaded by this function.
// it returns a Promise that will resolve once
// the image has finished loading
let loader = function (src) {
return new Promise(function (resolve, reject) {
let img = new Image();
img.onload = function () {
// resolve the promise with our url so it is
// returned in the result of Promise.all
resolve(src);
};
img.onerror = function (err) {
reject(err);
};
img.src = src;
});
};
// create an image loader for each url
let loaders = [];
images.forEach(function (image) {
loaders.push(loader(image));
});
// Promise.all will return a promise that will resolve once all of of our
// image loader promises resolve
return Promise.all(loaders);
}
// the images we are going to display
let myImages = [
'http://www.gifpng.com/400x200',
'http://www.gifpng.com/400x200/ffffff/000000',
'http://www.gifpng.com/400x200/000000/ffffff'
];
// $(document).ready(fn) is deprecated,
// use the $(fn) form instead
$(function() {
// after the images are loaded this will be called with an array of the loaded images
function cycleImages (images) {
let index = 0;
setInterval(function() {
// since we need an array of the image names to preload them anyway,
// just load them via JS instead of class switching so you can cut them
// out of the CSS and save some space by not being redundant
$('#backgrounds').css('backgroundImage', 'url("' + images[index] + '")');
// increment, roll over to 0 if at length after increment
index = (index + 1) % images.length;
}, 750);
}
// load the images and start cycling through them after they are loaded
loadImages(myImages).then(cycleImages).catch(function (err) {
console.error(err);
});
});
#backgrounds {
height: 200px;
width: 400px;
border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="backgrounds"></div>
Edit:
Taking Just a student's comments into account, here is a version that will only switch the image once it is loaded, but do it right away if it is loaded. It also skips images that failed to load. Since cycleImages is no longer called via .then, I also changed it so it accepts a target element (as a jQuery object) along with an array of images promises. That way you can easily use this on multiple places on a page with different images sets if you wanted to.
function loadImages (images) {
// each image will be loaded by this function.
// it returns a Promise that will resolve once
// the image has finished loading
let loader = function (src) {
return new Promise(function (resolve, reject) {
let img = new Image();
img.onload = function () {
// resolve the promise with our url
resolve(src);
};
img.onerror = function (err) {
reject(err);
};
img.src = src;
});
};
// return an array of image-loading promises
return images.map(function (image) {
return loader(image);
});
}
// the images we are going to display
let myImages = [
'http://www.gifpng.com/400x200',
'http://www.invalid-domain-name.foo/this-url-certainly-does-not-exist.jpg',
'http://www.gifpng.com/400x200/ffffff/000000',
'http://www.gifpng.com/400x200/000000/ffffff'
];
// $(document).ready(fn) is deprecated,
// use the $(fn) form instead
$(function() {
// this receives an array of the promises for each image
function cycleImages ($target, images) {
let index = 0,
interval = 750; // how many ms to wait before attempting to switch images
function nextImage () {
// p is the promise for the current image
let p = images[index],
next = function (wait) {
// increment our counter and wait to display the next one
index = (index + 1) % images.length;
setTimeout(nextImage, wait);
};
// wait for this image to load or fail to load
p.then(function (src) {
// it loaded, display it
$target.css('backgroundImage', 'url("' + src + '")');
next(interval);
}).catch(function (err) {
// this one failed to load, skip it
next(0);
});
}
// start cycling
nextImage();
}
// load the images and start cycling through them as they are loaded
cycleImages($('#backgrounds'), loadImages(myImages));
});
#backgrounds {
height: 200px;
width: 400px;
border: 1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="backgrounds"></div>
Instead of hard-coding the interval between images changes you could also pass that in as a parameter. At that point though, I would refactor it to use a config object to pass everything but the image promise array: cycleImages(myImages, {target: $('#backgrounds'), interval: 1000});
You can load them in javascript. Like:
(new Image()).src = url1;
(new Image()).src = url2;
(new Image()).src = url3;
Change the "url1", "url2", "url3" with you own image urls. Browser will load the image but they wont be visible anywhere.
Interesting idea.
Could you potentially load them in the background so like:
<div class="hidden-images">
<img src="img1.jpg" />
<img src="img2.jpg" />
<img src="img3.jpg" />
</div>
Then in CSS
.hidden-images {
position: relative;
z-index: 1;
}
Then on whatever you sort of main container div is, slap the following on it?
.main-container {
position: relative;
z-index: 2; // Or higher, whatever is needed
}
You can do also do this with different html and css structure like this.
html
<div class="backgrounds">
<div class="background freddy"></div>
</div>
css
.backgrounds{
background-color: transparent;
background-position: center bottom;
background-repeat: no-repeat;
background-size: 130%;
height: 250px;
width; 100%;
}
.backgrounds .freddy{
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/hero.jpg');
}
.backgrounds .irene{
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/projects-blur.png');
}
.backgrounds .joe{
height: 100%;
width: 100%;
background-image: url('http://adrianweb.net/includes/images/contacts-blur.png');
}
JS
$(document).ready(function(){
setInterval(function() {
if ( $('.background').hasClass('freddy') ){
$('.background').removeClass('freddy').addClass('irene');
} else if ( $('.background').hasClass('irene') ){
$('.background').removeClass('irene').addClass('joe');
} else if ( $('.background').hasClass('joe') ){
$('.background').removeClass('joe').addClass('freddy');
}
}, 750);
});
Maybe you can add transition/animation to make it look fade in and out.
Sample codepen below:
http://codepen.io/adrianrios/pen/yMEpEv

Using Lightbox2 to display video

Site: http://blieque.comli.com/motion
Before I start, I know there are many alternatives to Lightbox2 (Lokesh Dhakar's) to display videos but I want to avoid using three different JavaScript things, already using MooTools and JQuery as I'd like to keep HTTP requests to a minimum, as well as disk usage.
As it comes, Lightbox2 does not support videos, full stop. However, I noticed that the JavaScript was essentially taking the contents of an a's href attribute and placing it in an img's src attribute when the light-box was open. As far as I can see, changing this img to an iframe (done) and setting the anchor tag's href to youtube.com/embed/*video-id* should generate an iframe containing that YouTube video (replacing watch?id= with embed/ presents a full screen version of the video.
I then also added JavaScript for width, height and frameborder attributes on top of the default class="lb-image". Now when the page is loaded and the Lightbox called it creates an empty window. If you inspect the code you can see all the attributes are there but the page in the frame frame isn't loaded, just creating an empty head and body tag.
I was just wondering if it was a server problem or a code problem and if so, how to fix it. If there any way to make it work?
Thanks
Note: I'm NOT using Drupal so the lightvideo option is not available.
Maybe someone still is looking for solution.
I had same problem in my project. Not with youtube iframe but it’s not difficult to implenet it. Lightbox2 cannot be extended so i wrote simple class whose adding listener and observer. For proper displaing require is that video have a poster with the same sizes. That’s the fastest way to keep corret sizes of popup.
In href required is to add dataset href with image url
<a href="POSTER_URL" data-href="VIDEO_URL" data-lightbox="Videos">
Open Lightbox
</a>
SCSS to cover image in popup and set fade effect while loading
.lightbox {
.lb {
&-outerContainer {
video {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 9999;
width: 100%;
height: auto;
opacity: 1;
transition: opacity 300ms ease-in-out;
border: none;
outline: none;
&:hover, &:focus {
border: none;
outline: none;
}
}
&.animating {
video {
opacity: 0;
}
}
}
&-container {
position: relative;
.lb-image {
border: none;
}
}
}
}
And JS class which one create and set video into popup. Maybe little messy but i don't care. It's only quick solution.
class LightBoxVideo {
constructor() {
this.videos = {};
this.lightBoxVideo();
}
lightBoxVideo = () => {
this.setEvents();
this.setMutationObserver();
}
setMutationObserver = () => {
const observer = new MutationObserver(mutation => {
const imageMutations = mutation.filter((m) => {
return m.attributeName === "src" && m.target.className === 'lb-image'
});
const overlayDisplay = window.getComputedStyle(document.querySelector('.lightboxOverlay'), null).display;
if("none" === overlayDisplay) {
this.removeVideoElement();
}
if(imageMutations.length > 0) {
if(this.videos[imageMutations[0].target.src]) {
this.removeVideoElement();
this.setVideoElement(this.videos[imageMutations[0].target.src]);
}
}
});
observer.observe(document.body, {
childList: false,
attributes: true,
subtree: true,
characterData: false
});
}
setEvents = () => {
const videoLinks = this.findVideoLinks();
videoLinks.forEach((link) => {
this.videos[link.href] = link;
link.addEventListener('click', (e) => {
this.removeVideoElement();
this.setVideoElement(e.target);
});
});
}
setVideoElement = (element) => {
const lightbox = document.querySelector('.lightbox')
const container = lightbox.querySelector('.lb-container');
const videoElement = this.createVideoElement(element);
container.prepend(videoElement);
}
removeVideoElement = () => {
const lightbox = document.querySelector('.lightbox')
const container = lightbox.querySelector('.lb-container');
const video = container.querySelector('video');
if(video) {
container.removeChild(video);
}
}
createVideoElement = (element) => {
const video = document.createElement('video');
video.setAttribute('poster', element.href);
video.setAttribute('controls', 'true');
const source = document.createElement('source');
source.setAttribute('src', element.dataset.href);
source.setAttribute('type', 'video/mp4');
video.append(source);
return video;
}
findVideoLinks = () => {
const hrefs = document.querySelectorAll('a[data-lightbox]');
const regex = /\.(mp4|mov|flv|wmv)$/;
if(0 === hrefs.length) {
return [];
}
return Array.from(hrefs).filter((href) => {
return !! href.dataset.href.match(regex);
});
}
}
To preview how it's work - codepen here: https://codepen.io/PatrykN/pen/RwKpwMe
Enabling video support
By default, support for videos is disabled. However, this can be easily enabled by checking the enable video support option on admin/settings/lightbox2.
Basic Example
This Video
Reference

Categories

Resources