I have three modals containing a video. Each button opens the same modal but it shows a different video depending on the buttom you clicked. It works fine, but I want to shut off the sound when closing the modal. There is a function to do that named closeVideo(), but it only works for the first modal, and not for the others.
Is there a way to create just one function to shut off the sound of every modal?
<div class="box">
<div class="piece">
<h2>Medcom</h2>
<div class="xbox">
<a class="ibutton trigger">
<h3>Ver video</h3>
</a>
</div>
</div>
</div>
<div class="box">
<div class="piece">
<h2>Juan Valdez</h2>
<div class="xbox">
<a class="ibutton trigger">
<h3>Ver video</h3>
</a>
</div>
</div>
</div>
<div class="box">
<div class="piece">
<h2>Grupo Epasa</h2>
<div class="xbox">
<a class="ibutton trigger">
<h3>Ver video</h3>
</a>
</div>
</div>
</div>
<div class="modal">
<div class="modal-content">
<span class="btn-close" (click)='closeVideo()'>
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe id="player" width="560" height="315" src="https://www.youtube.com/embed/N8ABAZvh8WE" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<div class="modal">
<div class="modal-content">
<span class="btn-close" (click)='closeVideo()'>
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe id="player" width="560" height="315" src="https://www.youtube.com/embed/rn937OyA00g" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<div class="modal">
<div class="modal-content">
<span class="btn-close" (click)='closeVideo()'>
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe id="player" width="560" height="315" src="https://www.youtube.com/embed/EUjWFr3w7RU" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
/* SHOW MODALS */
const triggers = document.getElementsByClassName('trigger');
const triggerArray = Array.from(triggers).entries();
const modals = document.getElementsByClassName('modal');
const closeButton = document.getElementsByClassName('btn-close');
const myPlayer = document.getElementById('player');
for (let [index, trigger] of triggerArray) {
let triggerIndex = index;
function toggleModal() {
modals[triggerIndex].classList.toggle('show-modal');
}
trigger.addEventListener("click", toggleModal);
closeButton[triggerIndex].addEventListener("click", toggleModal);
}
closeVideo(){
myPlayer.setAttribute("src", " ");
console.log('video is closed now!');
}
The reason that you're having trouble is due to the repetition of the id across all <iframe> elements; as any id can be used only once within an HTML document JavaScript will only ever look for precisely one element with a given id; hence in this case if it finds an element with id="player" it will not look for any other element with that id attribute-value, since none should exist.
One solution, therefore, is to change the id to a class and therefore JavaScript will look for all elements with that class-name, then you could iterate through those elements and hide all of them:
closeVideo(){
let myPlayers = document.querySelectorAll('.player');
// here we use NodeList.forEach() to iterate through the
// NodeList returned by document.querySelectorAll(), and
// use an Arrow function as the callback:
myPlayers.forEach(
// here 'player' is the current element of the NodeList
// over which we're iterating; and we set the src of each
// element of the NodeList in turn to an empty string:
(player) => player.setAttribute('src','')
);
console.log('video is closed now!');
}
Of course this requires the HTML to be updated, to the following:
<div class="modal">
<div class="modal-content">
<span class="btn-close" (click)='closeVideo()'>
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<!-- note the change from id="player" to class="player"
this is true of all elements, though for brevity I'm
only showing one element -->
<iframe class="player" width="560" height="315" src="https://www.youtube.com/embed/N8ABAZvh8WE" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
However, once this is done you still have three modal elements, each with a different video within it. It's important to remember that one of the general principle of most programming is that of "DRY": Don't Repeat Yourself.
With that in mind, especially given the repetition of the elements themselves, it seems easier to use the <button> elements to pass in the relevant data, taking advantage of custom data-* attributes, such as:
<!-- here we use the data-video-id attribute to hold the video's id,
the data-platform-base attribute to hold the relevant base-URL
of the platform, and we use the videoPlayer function itself to
create the relevant src: -->
<button data-video-id="N8ABAZvh8WE" data-platform-base="https://www.youtube.com/embed/">Open: </button>
// here the event argument is passed in automatically by
// the later use of EventTarget.addEventListener(); we're using
// Arrow function syntax, since we have no need to use 'this'
// in the function:
const closeVideo = (event) => {
// event.target is the element that originally triggered
// the eventListener was listening for:
let closeButton = event.target,
// we use Element.closest() to find the first ancestor
// element of the event.target that matches the supplied
// CSS selector:
modal = closeButton.closest('div.modal'),
// from that modal element we use querySelector() to find
// the first of any elements that match the supplied CSS
// selector:
player = modal.querySelector('iframe');
// here we update the 'hidden' property of the modal element
// to true in order to hide the element:
modal.hidden = true;
// we update - or rather remove - the player's src, by setting
// that src to null (in order to stop the video and any sound
// from playing):
player.src = null;
},
// here we retrieve all the <button> elements that have a
// data-video-id attribute and which have a data-platform-base
// attribute:
buttons = document.querySelectorAll('button[data-video-id][data-platform-base]'),
// we find the modal element using document.querySelector() to search
// the document for the first of any elements that match the supplied
// CSS selector:
modal = document.querySelector('div.modal');
// here we use querySelector() again to search within the modal
// element (the div.modal element found above) to find the first
// <span> element with the class of 'btn-close', and use
// EventTarget.addEventListener() to bind the closeVideo() function
// (note the deliberate lack of parentheses on the function name)
// as the event-handler for the 'click' event:
modal.querySelector('span.btn-close').addEventListener('click', closeVideo);
// we use NodeList.forEach() to iterate over the NodeList returned by
// document.querySelectorAll():
buttons.forEach(
// using Arrow function syntax:
// we again use EventTarget.addEventListener() to bind the
// anonymous function as the event-handler for the 'click'
// event:
(button) => button.addEventListener('click', () => {
// here we cache a reference to the button.dataset DOMStringMap
// of custom data-* attributes and properties:
const data = button.dataset,
// here we create a url from the attribute-values held in the
// data-platform-base and data-video-id properties (note that
// these attribute-names are camel-cased as normal in JavaScript;
// these properties are retrieved within the jQuery-like
// ${...} and interpolated into the String:
url = `${data.platformBase}${data.videoId}`,
// we find the modal element:
modal = document.querySelector('div.modal'),
// we find the player element:
player = modal.querySelector('iframe');
// we update the src attribute of the player:
player.src = url;
// then we update the modal.hidden property to true, in order
// to show that element:
modal.hidden = false;
})
);
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/*
here we position the modal element centred on the page,
and with a low z-index to keep it below the page content:
*/
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: -1000;
}
/*
here we update the z-index to a high number, in order to lift
it above the page-content; the selector here selects:
- all elements with a class of 'modal',
- which do not have the hidden attribute, using the
attribute-selector ([hidden]) within CSS' negation-operator
( :not() )
*/
.modal:not([hidden]) {
z-index: 1000;
}
/*
here we show the video id in the <button> elements:
*/
button::after {
content: attr(data-video-id);
}
span.btn-close {
display: inline-block;
cursor: pointer;
border: 2px solid #aaa;
width: 3ex;
height: 3ex;
text-align: center;
line-height: 3ex;
}
<!-- Note the addition of the 'hidden' attribute, below: -->
<div class="modal" hidden>
<div class="modal-content">
<span class="btn-close">X</span>
<iframe id="player" width="560" height="315" src="https://www.youtube.com/embed/N8ABAZvh8WE" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<button data-video-id="N8ABAZvh8WE" data-platform-base="https://www.youtube.com/embed/">Open: </button>
<button data-video-id="rn937OyA00g" data-platform-base="https://www.youtube.com/embed/">Open: </button>
<button data-video-id="EUjWFr3w7RU" data-platform-base="https://www.youtube.com/embed/">Open: </button>
JS Fiddle demo.
[In] case, I don't want to apply the 'DRY' concept and keep several modals in my HTML, Is there a way to use the forEach() function, and still be able to open and see the other videos? I mean, after iterating the elements with class 'player' ? because I have seen it makes all the remaining videos hidden. Just curious about it.
Comment from OP, below: How can I make one function to close modals with different content in JS.
I'm not entirely sure that I understood the request on first reading, my first thoughts are below. This approach emulates the first approach (above), in that only one video/'modal' is visible at any one time. If I've misunderstood please clarify in the comments below and I'll attempt to improve the answer to meet your needs.
// here we define a 'modals' Object that serves as a
// repository of functions and controls for the
// interactivity:
const modals = {
// a 'buttons' Object that caches the relevant
// control elements:
buttons: {
show: document.querySelectorAll('.trigger'),
hide: document.querySelectorAll('.btn-close'),
},
// functions, which serve to provide the interactivity;
// here we have an EventObject ('evt') passed to the
// show() function (passed automatically by the later
// use of EventTarget.addEventListener()):
show: function(evt) {
// EventObject.currentTarget returns the element to
// which the event-listener was bound
// (EventObject.target returns the element that
// triggered the listened-for action, which may be
// the element itself or a descendant of that element):
let clicked = evt.currentTarget,
// we retrieve the relevant index from the element's
// dataset (we assign that value in later code) and
// and cast it to an integer, with parseInt(), in
// base-10 (hence the '10' as the second argument):
i = parseInt(clicked.dataset.index, 10),
// here we retrieve the elements with a class of
// 'modal':
modalBoxes = document.querySelectorAll('.modal');
// iterating over those elements using
// NodeList.prototype.forEach():
modalBoxes.forEach(
// we pass two arguments into the function, both
// of which are available automatically; 'modal'
// is a reference to the current Node of the
// NodeList over which we're iterating, and 'index'
// is the index of the current Node within the
// NodeList itself.
// in the function we're updating the modal.hidden
// property, we want to show the modal if its index
// matches; so if the i is not equal to index the
// modal is hidden, otherwise the modal is shown by
// as its index is equal to 'i' and therefore the
// assessment is false, so hidden its hidden property
// is false, therefore it's shown:
(modal, index) => modal.hidden = i !== index
);
},
hide: (evt) => {
// here we're navigating from the element to which
// the event-listener was bound up to the first
// ancestor element with the class of 'modal':
const m = evt.currentTarget.closest('.modal'),
// from there we retrieve the <iframe> element:
iframe = m.querySelector('iframe');
// here we hide the modal:
m.hidden = true;
// here we update the src of the <iframe> to be equal
// its own src; this causes a reload of the content
// and as such stops it from playing:
iframe.src = iframe.src;
},
// an initialisation function:
init: function() {
// here we're using destructuring to assign the
// 'hide' and 'show' properties of the
// this.buttons Object to variables of the same
// name:
const {
show,
hide
} = this.buttons;
// here we retrieve the NodeList of '.modal' elements
// and then iterate over them:
document.querySelectorAll('.modal').forEach(
(modal) => {
// we're initially hiding all '.modal' elements:
modal.hidden = true;
});
// iterating over the 'show' elements:
show.forEach(
// passing in two arguments, 'btn' is the reference
// to the current Node in the NodeList, and 'i' is
// the current Node's index in that NodeList:
(btn, i) => {
// here we set the element's data-index attribute by
// setting its dataset.index property to be equal to
// the index of the current 'show' element:
btn.dataset.index = i;
// binding the function of the current modals Object
// as the event-handling function of the 'click' event:
btn.addEventListener('click', this.show);
});
// similar to the above, but binding the hide() function
// of the modals Object as the event-handler for the
// 'click' event on each of the 'hide' elements:
hide.forEach(
(btn) => {
btn.addEventListener('click', this.hide);
});
},
};
// initialising the functionality:
modals.init();
*,
::before,
::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
main {
width: 80%;
margin: 0 auto;
}
header {
display: flex;
justify-content: space-between;
}
.box {
text-align: center;
}
<main>
<header>
<div class="box">
<div class="piece">
<h2>Medcom</h2>
<div class="box">
<button class="ibutton trigger">
<h3>Ver video</h3>
</button>
</div>
</div>
</div>
<div class="box">
<div class="piece">
<h2>Juan Valdez</h2>
<div class="xbox">
<button class="ibutton trigger">
<h3>Ver video</h3>
</button>
</div>
</div>
</div>
<div class="box">
<div class="piece">
<h2>Grupo Epasa</h2>
<div class="xbox">
<button class="ibutton trigger">
<h3>Ver video</h3>
</button>
</div>
</div>
</div>
</header>
<div class="videoContent">
<div class="modal">
<div class="modal-content">
<span class="btn-close">
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe class="player" width="560" height="315" src="https://www.youtube.com/embed/N8ABAZvh8WE" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<div class="modal">
<div class="modal-content">
<span class="btn-close">
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe class="player" width="560" height="315" src="https://www.youtube.com/embed/rn937OyA00g" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
<div class="modal">
<div class="modal-content">
<span class="btn-close">
<img src="../../../../../assets/img/close.svg" alt="close">
</span>
<iframe class="player" width="560" height="315" src="https://www.youtube.com/embed/EUjWFr3w7RU" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
</div>
</main>
JS Fiddle demo.
Reference:
CSS:
Attribute selectors ([attribute]).
Negation (:not()) pseudo-class.
HTML:
hidden.
JavaScript:
Arrow function expressions.
Destructuring assignment {a,b} = ObjectName.
document.querySelector().
Element.closest().
Element.setAttribute().
Event.currentTarget
Event.target.
EventTarget.addEventListener().
HTMLElement.hidden.
HTMLOrForeignElement.dataset.
NodeList.prototype.forEach().
Template literals (Template strings).
you can perform the following function in JavaScript
$(function(){
$('.modal').hide();
});
o
Save it in an array and then loop through that array and close one by one with adding the hide class
$(function(){
var arrayModals=$('.modal');
for(var modalObj:arrayModals){
//do stuff here
$(document.find(modalobj).getId()).toggleClass('visible');
}
}):
only do it in the event you want either by clicking or detecting the end of a video
How would I be able to add a YouTube embedded iframe inside of a Divbox using a text inputfield, button to submit and javascript?
Thank you.
By taking "Divbox" to mean "div" I have made an attempt at an answer:
You could do the following if the user knows the video ID (the string of characters at the end of a youtube url):
document.getElementById("b").onclick = function(d) {
var full = '<iframe width="560" height="315" src="https://www.youtube.com/embed/' + document.getElementById("inp").value + '" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'
document.getElementById("frame").innerHTML = full;
}
<button id="b">Go!</button>
<input id="inp" value="MPZI2M1fDi8"/>
<div id="frame"></div>
I'm trying to play a video on a seperate div on link click but whenever I click the link instead of playing inside the div it plays on full screen and the link changes to the file path.
$('.link').on('click', function(event) {
event.preventDefault();
var url =$(this).attr("href");
$("#frame").attr("src", url);
})
<div class="live1452">
<iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen id="frame"></iframe>
</div>
<div class="title">
{{l.title}}
</div>
It seems as if it following the link, it shouldn't as you are calling event.preventDefault(), if there were other events wrapping that one you should have to also call event.stopPropagation() but we lack information to know if this could be the case.
I would not use iframe for this, you could try using the html5 video element instead, here is a code snippet:
$('.link').on('click',function(event) {
event.preventDefault();
event.stopPropagation();
var url =$(this).attr("href");
$("#video").attr("src", url);
$("#video")[0].play();
});
video {
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="live1452">
<video controls allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen id="video"></video>
</div>
<div class="title">
Sample video
</div>
I have two soundcloud widgets hidden on my page. I have linked them to divs that act as play buttons. Currently, when the first div is clicked, the first sound starts. When the second div is clicked, the second sound starts and the first sound is automatically paused. I want the sounds to play simultaneously.
<div id = "bgSound">
<iframe id = 'first' width="0" height="0" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/257485031?secret_token=s-i9opu&color=ff5500&inverse=false&auto_play=false&show_user=true"></iframe>
<iframe id = "second" width="0" height="0" scrolling="no" frameborder="no" src="https://api.soundcloud.com/tracks/257498359?secret_token=s-c9D5D&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true"></iframe>
</div>
var widget1 = SC.Widget('first');
var widget2 = SC.Widget('second')
$('.Top-Left').click(function() {
widget1.play();
});
$('.Top-Center').click(function() {
widget2.play();
});
You should be able to use single_active=false to ensure both can play at the same time, otherwise one will pause the other when it starts playing.
<div id = "bgSound">
<iframe id="first" width="0" height="0" scrolling="no" frameborder="no" src="https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/257485031?secret_token=s-i9opu&color=ff5500&inverse=false&auto_play=false&show_user=true&single_active=false"></iframe>
<iframe id="second" width="0" height="0" scrolling="no" frameborder="no" src="https://api.soundcloud.com/tracks/257498359?secret_token=s-c9D5D&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&visual=true&single_active=false"></iframe>
</div>
var widget1 = SC.Widget('first');
var widget2 = SC.Widget('second')
$('.Top-Left').click(function() {
widget1.play();
});
$('.Top-Center').click(function() {
widget2.play();
});
SoundCloud Widget Parameter Documentation
I am using fullpage.js for my website. About fullpage.js everything working fine. I want to open an div on click of pagniation. I am able to open div on click which contains YouTube video. But when div is visible browser scroll will be disabled I am not getting where I go wrong.
here is sample of my code:
$(document).ready(function() {
$('#fullpage').fullpage({
verticalCentered: false,
css3:false,
navigation: true,
normalScrollElements: '.text',
navigationTooltips: ['tooltip1', 'tooltip2', 'tooltip3'],
onLeave: function(index, nextIndex, direction){
var prevIndex = index - 1;
var currentIndex = nextIndex - 1;
$('#section'+currentIndex).css('transform', 'scale(1.2)');
$('#section'+currentIndex).css('transition', 'all 5s ease-in');
setTimeout(function(){
$('#section'+prevIndex).css('transform', 'scale(1)');
$('#section'+prevIndex).css('transition', 'all 0.2s ease-in');
},500);
},
});
});
in jquery.fullpage.js
var li = '<li><span>' + tooltip + '</span>';
function :
function displayDetails(id)
{
$('#text').show();
}
HTML code:
<style>
#text {
position: absolute;
display:none;
z-index:999;
top: 300px;
width:100%;
height: auto;
}
</style>
<div id="text">
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/6iNFimn4wFA" frameborder="0" allowfullscreen></iframe>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/jsd-mNTIukM" frameborder="0" allowfullscreen></iframe><br>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/ylD-WFvRZkM" frameborder="0" allowfullscreen></iframe>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/g2YzRTAstPo" frameborder="0" allowfullscreen></iframe><br>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/98NqtVG-hFU" frameborder="0" allowfullscreen></iframe>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/O4weBTRCFzU" frameborder="0" allowfullscreen></iframe><br>
<iframe width="480" height="270" class="video" src="https://www.youtube.com/embed/UVzEOsHT7XA" frameborder="0" allowfullscreen></iframe>
</div>
<div id="fullpage">
<div class="section" id="section0"></div>
<div class="section" id="section1"></div>
<div class="section" id="section2"></div>
<div class="section" id="section3"></div>
<div class="section" id="section4"></div>
</div>
This code opens div as expected but scrolling to see last contents is not working. Instead of video if I enter text scroll works perfectly. As soon as I add video in div scroll stops working. I haven't set overflow property for #text. Please help me out.
I found this worked
function testing(){
//forcing fullPage.js to recalculate dimensions.
setTimeout(function(){
fullpage_api.reBuild();
}, 500);
};
for me this didn't work
$.fn.fullpage.reBuild();
First of all, you should be using scrollOverflow:true rather than normalScrollElements.
scrollOverflow:
(default false) defines whether or not to create a scroll for the section in case its content is bigger than the height of it. When set to true, your content will be wrapped by the plugin. Consider using delegation or load your other scripts in the afterRender callback. In case of setting it to true, it requires the vendor plugin jquery.slimscroll.min and it should be loaded before the fullPage.js plugin. For example:
Then, fullPage.js is calculatingn the height of your sections on page load.
If the videos are not displayed on page load, fullPage.js won't know the content is changing until you tell it so.
Try this:
function displayDetails(id){
$('#text').show();
//forcing fullPage.js to recalculate dimensions.
$.fn.fullpage.reBuild();
}
More about the reBuild method in the docs:
reBuild()
Updates the DOM structure to fit the new window size or its contents.
Ideal to use in combination with AJAX calls or external changes in the
DOM structure of the site.
See this example I made for you
For react users, this is the solution I found, that works best.
You need to save the fullpageApi from the render method, in state.
And then, take advantage of the useEffect to call the rebuild method.
I was using it inside the render(), but then I was getting a lot of weird behaviours, because the rebuild() was being called in every other page state update.
const [fullPageApi, setFullPageApi] = useState<fullpageApi>();
useEffect(() => {
if (fullPageApi){
setTimeout(function () {
fullPageApi.reBuild();
}, 1000);
}
}, [fullPageApi]);
// (...)
// Component example
<ReactFullpage
scrollOverflow
navigation
render={({ fullpageApi }) => {
if (!fullPageApi) setFullPageApi(fullpageApi);
return (
<ReactFullpage.Wrapper>
<YourComponent />
</ReactFullpage.Wrapper>
);
}
}
/>