javascript - 如何在 JS 中创建一个函数来关闭具有不同内容的模态
问题描述
我有三个包含视频的模态。每个按钮都会打开相同的模式,但会根据您单击的按钮显示不同的视频。它工作正常,但我想在关闭模式时关闭声音。有一个名为 closeVideo() 的函数可以执行此操作,但它仅适用于第一个模态,而不适用于其他模态。有没有办法只创建一个函数来关闭每个模态的声音?
<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!');
}
解决方案
您遇到麻烦的原因是由于id
所有<iframe>
元素的重复;因为 anyid
只能在 HTML 文档中使用一次,所以 JavaScript 只会查找具有给定 的元素id
;因此在这种情况下,如果它找到具有该属性值的元素,id="player"
它将不会寻找具有该id
属性值的任何其他元素,因为不应该存在。
因此,一种解决方案是将 更改id
为 a class
,因此 JavaScript 将查找具有该类名的所有元素,然后您可以遍历这些元素并隐藏所有元素:
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!');
}
当然,这需要将 HTML 更新为以下内容:
<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>
但是,完成此操作后,您仍然拥有三个模态元素,每个元素中都有不同的视频。重要的是要记住,大多数编程的一般原则之一是“DRY”:不要重复自己。
<button>
考虑到这一点,特别是考虑到元素本身的重复,使用元素来传递相关数据似乎更容易,利用自定义data-*
属性,例如:
<!-- 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>
[在] 情况下,我不想应用 'DRY' 概念并在我的 HTML 中保留几个模式,有没有办法使用该
forEach()
功能,并且仍然能够打开并查看其他视频?我的意思是,在使用类 'player' 迭代元素之后?因为我已经看到它隐藏了所有剩余的视频。只是好奇而已。
来自 OP 的评论,如下:如何创建一个函数来关闭 JS 中具有不同内容的模态。
我不完全确定我在初读时理解了这个要求,我的第一个想法如下。这种方法模拟了第一种方法(上图),因为在任何时候都只有一个视频/“模态”可见。如果我误解了,请在下面的评论中澄清,我会尝试改进答案以满足您的需求。
// 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>
参考:
- CSS:
- 属性选择器 (
[attribute]
)。 - 否定(
:not()
)伪类。 - HTML:
hidden
.- JavaScript:
- 箭头函数表达式。
- 解构赋值
{a,b} = ObjectName
。 document.querySelector()
.Element.closest()
.Element.setAttribute()
.Event.currentTarget
Event.target
.EventTarget.addEventListener()
.HTMLElement.hidden
.HTMLOrForeignElement.dataset
.NodeList.prototype.forEach()
.- 模板文字(模板字符串)。
推荐阅读
- python - 将 gmsh 生成的 3D 网格导入 fipy 时出错
- teradata - 查询根据条件返回第一行
- reactjs - React material-ui 无法读取 null 的属性“方向”
- c++ - 如何使用 GStreamer 播放 PCM 数据数组
- html - 如何边距自定义滚动条?
- java - 错误:(251, 5) 错误:资源 'attr/pivotX' 的重复值与配置 '
- javascript - 如何在java脚本中选择分配给Django变量的html id
- xamarin.forms - Xamarin formsCarousel View with Text, Image in the center of the screen with Dots indicator in below
- java - 正则表达式如果行不包含关键字
- matlab - matlab精度处理大数