Change html structure to get get rid of id's
This commit is contained in:
1 parent
6bd456764e
commit
fa97f2c369
4 files changed
+147
-94
No files matched your search
@@ -2,9 +2,11 @@
|
||||
|
||||
Snappy is yet another carousel library.
|
||||
Its goal is to be as lightweight and performant as possible.
|
||||
|
||||
It does not implement every feature found in other carousel libraries. Instead, it relies on native CSS scroll snapping and adds as little on top as possible.
|
||||
|
||||
Mouse-drag behavior closely follows native touch scrolling in the browser, including inertia and snapping to a slide.
|
||||
Mouse-drag behavior closely follows native touch scrolling in the browser, including inertia and snapping to a slide. Thus dragging with the mouse feels pretty identical to native scrolling on a touch device or using a trackpad.
|
||||
|
||||
|
||||
| File | Size |
|
||||
| ---------- | ------: |
|
||||
@@ -14,7 +16,8 @@ Mouse-drag behavior closely follows native touch scrolling in the browser, inclu
|
||||
|
||||
## Usage
|
||||
|
||||
### CSS only
|
||||
|
||||
### CSS only for a basic scroll container
|
||||
|
||||
You can use Snappy without JavaScript. This gives you a simple CSS-only scroll container with snapping slides.
|
||||
|
||||
@@ -23,17 +26,55 @@ You can use Snappy without JavaScript. This gives you a simple CSS-only scroll c
|
||||
<link rel="stylesheet" href="dist/snappy.css">
|
||||
|
||||
<!-- add this structure -->
|
||||
<div id="demo-carousel" class="snappy-carousel">
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-carousel">
|
||||
<div class="scroll-container">
|
||||
<div class="slide">1</div>
|
||||
<div class="slide">2</div>
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Snappy is mostly unstyled, leaving the appearance up to you. For inspiration, see the [example](index.html).
|
||||
Snappy is mostly unstyled, leaving the appearance up to you.
|
||||
For inspiration, see the [complete example](index.html).
|
||||
|
||||
### Add JavaScript
|
||||
|
||||
### Options
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scroll_snap
|
||||
add data attribites to the carousel container to change the default behavior:
|
||||
|
||||
```html
|
||||
<!--
|
||||
Options
|
||||
-------
|
||||
|
||||
data-direction="x" (default)
|
||||
data-direction="y"
|
||||
|
||||
data-gap="var(--size-1)"
|
||||
data-gap="10px"
|
||||
data-gap="1rem"
|
||||
|
||||
data-snap-stop="always"
|
||||
data-snap-stop="normal" (default)
|
||||
|
||||
data-snap-align="center" (default)
|
||||
data-snap-align="start"
|
||||
data-snap-align="end"
|
||||
-->
|
||||
<div class="snappy-carousel"
|
||||
data-gap="1rem"
|
||||
data-snap-align="start">
|
||||
|
||||
<div class="scroll-container">
|
||||
<div class="slide">1</div>
|
||||
<div class="slide">2</div>
|
||||
...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
### Add JavaScript for a carousel
|
||||
|
||||
Including the JavaScript adds the following features:
|
||||
|
||||
@@ -42,6 +83,18 @@ Including the JavaScript adds the following features:
|
||||
- Mouse dragging
|
||||
|
||||
```html
|
||||
<div class="snappy-carousel" data-gap="1rem" data-snap-align="start">
|
||||
<div class="scroll-container">
|
||||
<div class="slide">1</div>
|
||||
<div class="slide">2</div>
|
||||
...
|
||||
</div>
|
||||
|
||||
<button class="prev">❮</button>
|
||||
<button class="next">❯</button>
|
||||
<div class="pagination"></div>
|
||||
</div>
|
||||
|
||||
<script src="dist/snappy.js" defer></script>
|
||||
```
|
||||
|
||||
|
||||
+25
-33
@@ -30,13 +30,14 @@
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* Custom snappy styling */
|
||||
/* Colors from Open color (https://yeun.github.io/open-color) */
|
||||
.snappy-carousel {
|
||||
counter-reset: snappy-slide;
|
||||
.scroll-container {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.snappy-slide {
|
||||
counter-increment: snappy-slide;
|
||||
.slide {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -53,7 +54,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.snappy-slide:nth-child(even) {
|
||||
.slide:nth-child(even) {
|
||||
background-color: #e9ecef;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -62,18 +63,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
.snappy-slide:before {
|
||||
content: counter(snappy-slide);
|
||||
}
|
||||
|
||||
.snappy-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.snappy-button {
|
||||
.prev,
|
||||
.next {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
font-size: 1rem;
|
||||
@@ -97,11 +88,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.snappy-pagination {
|
||||
.pagination {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.snappy-pagination-dot {
|
||||
.pagination .dot {
|
||||
width: 0.66rem !important;
|
||||
height: 0.66rem !important;
|
||||
border-radius: 0.33rem;
|
||||
@@ -115,14 +106,15 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.snappy-pagination-dot:hover {
|
||||
.pagination .dot:hover {
|
||||
background-color: #adb5bd !important;
|
||||
}
|
||||
|
||||
.snappy-pagination-dot.is-active {
|
||||
.pagination .dot.is-active {
|
||||
background-color: #1971c2 !important;
|
||||
width: 2rem !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="dist/snappy.css">
|
||||
</head>
|
||||
@@ -151,21 +143,21 @@
|
||||
data-snap-align="start"
|
||||
data-snap-align="end"
|
||||
-->
|
||||
<div id="demo-carousel" class="snappy-carousel" data-gap="1rem" data-snap-align="start">
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-slide"></div>
|
||||
<div class="snappy-carousel" data-gap="1rem" data-snap-align="start">
|
||||
<div class="scroll-container">
|
||||
<div class="slide">1</div>
|
||||
<div class="slide">2</div>
|
||||
<div class="slide">3</div>
|
||||
<div class="slide">4</div>
|
||||
<div class="slide">5</div>
|
||||
<div class="slide">6</div>
|
||||
<div class="slide">7</div>
|
||||
<div class="slide">8</div>
|
||||
</div>
|
||||
|
||||
<div class="snappy-controls">
|
||||
<button class="snappy-button snappy-prev" data-carousel="demo-carousel">❮</button>
|
||||
<button class="snappy-button snappy-next" data-carousel="demo-carousel">❯</button>
|
||||
<div class="snappy-pagination" data-carousel="demo-carousel"></div>
|
||||
<button class="prev">❮</button>
|
||||
<button class="next">❯</button>
|
||||
<div class="pagination"></div>
|
||||
</div>
|
||||
|
||||
<!-- Javascript is only needed for pagination and buttons -->
|
||||
|
||||
+34
-24
@@ -1,4 +1,6 @@
|
||||
.snappy-carousel {
|
||||
|
||||
.scroll-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
@@ -6,52 +8,60 @@
|
||||
user-select: none;
|
||||
cursor: grab;
|
||||
scroll-behavior: smooth;
|
||||
|
||||
&[data-direction="y"] {
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
scroll-snap-type: x mandatory;
|
||||
|
||||
&.is-dragging {
|
||||
scroll-snap-type: none;
|
||||
scroll-behavior: unset;
|
||||
}
|
||||
}
|
||||
|
||||
scroll-snap-type: x mandatory;
|
||||
|
||||
&[data-direction="y"] {
|
||||
.slide {
|
||||
flex-shrink: 0;
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: normal;
|
||||
}
|
||||
|
||||
.pagination .dot {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-color: silver;
|
||||
}
|
||||
|
||||
/* Options */
|
||||
|
||||
&[data-direction="y"] .scroll-container {
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&[data-direction="y"] .scroll-container {
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
scroll-snap-type: y mandatory;
|
||||
}
|
||||
|
||||
gap: var(--carousel-gap, 0);
|
||||
gap: attr(data-gap type(*), 0);
|
||||
&[data-gap] {
|
||||
--carousel-gap: attr(data-gap type(*), 0);
|
||||
|
||||
.snappy-slide {
|
||||
flex-shrink: 0;
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: normal;
|
||||
.scroll-container {
|
||||
gap: var(--carousel-gap, 0);
|
||||
}
|
||||
}
|
||||
|
||||
&[data-snap-align="start"] .snappy-slide {
|
||||
&[data-snap-align="start"] .slide {
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
&[data-snap-align="end"] .snappy-slide {
|
||||
&[data-snap-align="end"] .slide {
|
||||
scroll-snap-align: end;
|
||||
}
|
||||
|
||||
&[data-snap-stop="always"] .snappy-slide {
|
||||
&[data-snap-stop="always"] .slide {
|
||||
scroll-snap-stop: always;
|
||||
}
|
||||
|
||||
}
|
||||
.snappy-pagination-dot {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
background-color: silver;
|
||||
}
|
||||
+27
-29
@@ -16,7 +16,7 @@ class Snappy {
|
||||
|
||||
class Carousel {
|
||||
// DOM references
|
||||
element = null;
|
||||
scrollContainer = null;
|
||||
prevButton = null;
|
||||
nextButton = null;
|
||||
pagination = null;
|
||||
@@ -37,24 +37,23 @@ class Carousel {
|
||||
activeSlideIndex = 0;
|
||||
|
||||
constructor(element) {
|
||||
this.element = element;
|
||||
this.direction = this.element.dataset.direction === 'y' ? 'y' : 'x';
|
||||
this.scrollContainer = element.querySelector('.scroll-container');
|
||||
this.direction = element.dataset.direction === 'y' ? 'y' : 'x';
|
||||
const gap = element.dataset.gap;
|
||||
if (gap) this.scrollContainer.style.setProperty('--carousel-gap', gap);
|
||||
this.slides = this.scrollContainer.querySelectorAll('.slide');
|
||||
|
||||
const gap = this.element.dataset.gap;
|
||||
if (gap) this.element.style.setProperty('--carousel-gap', gap);
|
||||
this.prevButton = document.querySelector(`.prev`);
|
||||
this.nextButton = document.querySelector(`.next`);
|
||||
this.pagination = document.querySelector(`.pagination`);
|
||||
|
||||
this.slides = this.element.querySelectorAll('.snappy-slide');
|
||||
this.prevButton = document.querySelector(`.snappy-prev[data-carousel="${this.element.id}"]`);
|
||||
this.nextButton = document.querySelector(`.snappy-next[data-carousel="${this.element.id}"]`);
|
||||
this.pagination = document.querySelector(`.snappy-pagination[data-carousel="${this.element.id}"]`);
|
||||
|
||||
this.element.addEventListener('mousedown', this.startDrag.bind(this));
|
||||
this.element.addEventListener('mousemove', this.drag.bind(this));
|
||||
this.element.addEventListener('mouseup', this.endDrag.bind(this));
|
||||
this.element.addEventListener('mouseleave', this.endDrag.bind(this));
|
||||
this.element.addEventListener('touchstart', this.reset.bind(this));
|
||||
this.element.addEventListener('wheel', this.reset.bind(this));
|
||||
this.element.addEventListener('scroll', this.updateActiveSlide.bind(this));
|
||||
this.scrollContainer.addEventListener('mousedown', this.startDrag.bind(this));
|
||||
this.scrollContainer.addEventListener('mousemove', this.drag.bind(this));
|
||||
this.scrollContainer.addEventListener('mouseup', this.endDrag.bind(this));
|
||||
this.scrollContainer.addEventListener('mouseleave', this.endDrag.bind(this));
|
||||
this.scrollContainer.addEventListener('touchstart', this.reset.bind(this));
|
||||
this.scrollContainer.addEventListener('wheel', this.reset.bind(this));
|
||||
this.scrollContainer.addEventListener('scroll', this.updateActiveSlide.bind(this));
|
||||
|
||||
if (this.prevButton) {
|
||||
this.prevButton.addEventListener('click', () => this.prev());
|
||||
@@ -73,7 +72,7 @@ class Carousel {
|
||||
|
||||
startDrag(e) {
|
||||
this.isDragging = true;
|
||||
this.element.classList.add('is-dragging'); // Disable scroll snapping
|
||||
this.scrollContainer.classList.add('is-dragging'); // Disable scroll snapping
|
||||
this.velocity = 0;
|
||||
this.lastPosition = undefined;
|
||||
this.lastTime = undefined;
|
||||
@@ -113,7 +112,6 @@ class Carousel {
|
||||
}
|
||||
|
||||
const target = this.calculateDragTarget();
|
||||
const element = this.element;
|
||||
const distance = target - this.getScrollPosition();
|
||||
|
||||
this.velocity = -distance * (1 - this.inertia) / 10;
|
||||
@@ -159,19 +157,19 @@ class Carousel {
|
||||
}
|
||||
|
||||
getScrollPosition() {
|
||||
return this.direction === 'y' ? this.element.scrollTop : this.element.scrollLeft;
|
||||
return this.direction === 'y' ? this.scrollContainer.scrollTop : this.scrollContainer.scrollLeft;
|
||||
}
|
||||
|
||||
setScrollPosition(position) {
|
||||
if (this.direction === 'y') {
|
||||
this.element.scrollTop = position;
|
||||
this.scrollContainer.scrollTop = position;
|
||||
} else {
|
||||
this.element.scrollLeft = position;
|
||||
this.scrollContainer.scrollLeft = position;
|
||||
}
|
||||
}
|
||||
|
||||
calculateDragTarget() {
|
||||
const element = this.element;
|
||||
const element = this.scrollContainer;
|
||||
const isVertical = this.direction === 'y';
|
||||
const maxScrollPosition = isVertical
|
||||
? element.scrollHeight - element.clientHeight
|
||||
@@ -237,7 +235,7 @@ class Carousel {
|
||||
|
||||
reset() {
|
||||
this.isDragging = false;
|
||||
this.element.classList.remove('is-dragging');
|
||||
this.scrollContainer.classList.remove('is-dragging');
|
||||
this.velocity = 0
|
||||
}
|
||||
|
||||
@@ -245,7 +243,7 @@ class Carousel {
|
||||
this.slides.forEach((slide, index) => {
|
||||
// Populate the pagination container with dots for each slide
|
||||
let div = document.createElement('div');
|
||||
div.classList.add('snappy-pagination-dot');
|
||||
div.classList.add('dot');
|
||||
div.addEventListener('click', () => this.goToSlide(index));
|
||||
this.pagination.appendChild(div);
|
||||
|
||||
@@ -267,11 +265,11 @@ class Carousel {
|
||||
const marginStart = isVertical ? 'scrollMarginTop' : 'scrollMarginLeft';
|
||||
const marginEnd = isVertical ? 'scrollMarginBottom' : 'scrollMarginRight';
|
||||
const cssPixels = value => Number.parseFloat(value) || 0;
|
||||
const containerRect = this.element.getBoundingClientRect();
|
||||
const containerStyle = getComputedStyle(this.element);
|
||||
const containerRect = this.scrollContainer.getBoundingClientRect();
|
||||
const containerStyle = getComputedStyle(this.scrollContainer);
|
||||
const snapportStartEdge = containerRect[startEdge] +
|
||||
(isVertical ? this.element.clientTop : this.element.clientLeft);
|
||||
const clientExtent = isVertical ? this.element.clientHeight : this.element.clientWidth;
|
||||
(isVertical ? this.scrollContainer.clientTop : this.scrollContainer.clientLeft);
|
||||
const clientExtent = isVertical ? this.scrollContainer.clientHeight : this.scrollContainer.clientWidth;
|
||||
const snapportStart = snapportStartEdge + cssPixels(containerStyle[paddingStart]);
|
||||
const snapportEnd = snapportStartEdge + clientExtent - cssPixels(containerStyle[paddingEnd]);
|
||||
const snapportCenter = (snapportStart + snapportEnd) / 2;
|
||||
|
||||
Reference in new issue
Block a user