<scroll-snap-carousel alignment="center">
<iframe src="https://www.youtube.com/embed/-L_Jb70YPe4?si=EDWBYK8_hfrsQ78i" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/65EfTFUFDwI?si=aJErtJXMW-Q9svck" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/AKscVerlAvY?si=vhfwTzXharkrYgFr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/vrWwrtEHoVs?si=5W71jHooPdKedglK" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
<iframe src="https://www.youtube.com/embed/ZsL-7mxaQWU?si=wN5mdaulE9SOAzK7" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</scroll-snap-carousel>
:root {
align-items: center;
background: linear-gradient(45deg, #4094bf, #ff7c7c);
display: flex;
height: 100%;
justify-content: center;
}
iframe {
aspect-ratio: 16/9;
border-radius: 10px;
width: 75%;
}
xxxxxxxxxx
/**
* A custom element that creates a scrollable carousel with snap points.
* Based on an example from this article: https://web.dev/articles/css-scroll-snap
* This custom element does NOT utilize the Shadow DOM.
*
* @attr {'start' | 'center' | 'end' | 'none'} alignment - The alignment of the carousel scroll snap.
*/
class ScrollSnapCarousel extends HTMLElement {
/**
* Define the properties of the custom element.
*/
static get observedAttributes() {
return ['alignment']
}
/**
* Bind the necessary methods to the custom element instance.
*/
constructor() {
super()
this.alignment = 'start'
this.scrollToNextPage = this.scrollToNextPage.bind(this)
this.scrollToPreviousPage = this.scrollToPreviousPage.bind(this)
this.calculateGalleryItemSize = this.calculateGalleryItemSize.bind(this)
this.ingestChildren = this.ingestChildren.bind(this)
}
/**
* Called when an attribute on the custom element is changed.
*/
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'alignment') {
this.alignment = newValue
}
}
/**
* Called when the element is added to the DOM.
*/
connectedCallback() {
this.ingestChildren()
this.render()
this.gallery = this.querySelector('#paginated-gallery')
this.galleryScroller = this.gallery.querySelector('.gallery-scroller')
this.galleryScroller.appendChild(this.fragment)
this.gallery
.querySelector('button.next')
.addEventListener('click', this.scrollToNextPage)
this.gallery
.querySelector('button.previous')
.addEventListener('click', this.scrollToPreviousPage)
this.calculateGalleryItemSize()
window.addEventListener('resize', this.calculateGalleryItemSize)
}
/**
* Called when the element is removed from the DOM.
*/
disconnectedCallback() {
window.removeEventListener('resize', this.calculateGalleryItemSize)
}
/**
* Ingests the children of the custom element into a document fragment.
*/
ingestChildren() {
this.fragment = document.createDocumentFragment()
Array.from(this.children).forEach((child) => {
this.fragment.appendChild(child)
})
}
/**
* Calculates the size of each gallery item based on the first child element.
*/
calculateGalleryItemSize() {
const nodes = Array.from(this.galleryScroller.children)
const firstElement = nodes.find(
(node) => node.nodeType === Node.ELEMENT_NODE,
)
this.galleryItemSize = firstElement.clientWidth
}
/**
* Scroll the carousel to the previous page.
*/
scrollToPreviousPage() {
this.galleryScroller.scrollBy(-this.galleryItemSize, 0)
}
/**
* Scroll the carousel to the next page.
*/
scrollToNextPage() {
this.galleryScroller.scrollBy(this.galleryItemSize, 0)
}
/**
* Handles the rendering of the component markup and CSS.
*/
render() {
this.innerHTML = `
<style>
* {
scroll-behavior: smooth;
}
.gallery-scroller * {
margin: 0 16px;
min-width: 75%;
position: relative;
scroll-snap-align: ${this.alignment};
}
.gallery {
position: relative;
}
.gallery-scroller {
-webkit-overflow-scrolling: touch;
align-items: center;
display: flex;
overflow-x: scroll;
overflow-y: hidden;
scroll-snap-type: x mandatory;
}
button {
background-color: rgba(0,0,0,0.5);
background-position: 50% 50%;
background-repeat: no-repeat;
border-radius: 4px;
height: 30px;
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 30px;
z-index: 1;
}
button:hover {
cursor: pointer;
}
button.next {
background-image: url('data:image/svg+xml;charset=utf-8,<svg width="18" height="18" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg"><title>Shape</title><path d="M25.557 14.7L13.818 2.961 16.8 0l16.8 16.8-16.8 16.8-2.961-2.961L25.557 18.9H0v-4.2z" fill="%23FFF" fill-rule="evenodd"/></svg>');
right: 10px;
}
button.previous {
background-image: url('data:image/svg+xml;charset=utf-8,<svg width="18" height="18" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg"><title>Shape</title><path d="M33.6 14.7H8.043L19.782 2.961 16.8 0 0 16.8l16.8 16.8 2.961-2.961L8.043 18.9H33.6z" fill="%23FFF" fill-rule="evenodd"/></svg>');
left: 10px;
}
</style>
<div id="paginated-gallery" class="gallery">
<div class="gallery-scroller"></div>
<button class="previous" aria-label="Previous"></button>
<button class="next" aria-label="Next" ></button>
</div>
`
}
}
window.customElements.define('scroll-snap-carousel', ScrollSnapCarousel)
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.