davidjerleke / embla-carousel Goto Github PK
View Code? Open in Web Editor NEWA lightweight carousel library with fluid motion and great swipe precision.
Home Page: https://www.embla-carousel.com
License: MIT License
A lightweight carousel library with fluid motion and great swipe precision.
Home Page: https://www.embla-carousel.com
License: MIT License
Topic is self explanatory
Hi!
First of all, let me say that I really like embla-carousel. Thank you for sharing your awesome work!!
There's one little bug I discovered when I have two slides with width: 50% and containScroll is set to true. It doesn't appear if I have only one slide with width: 100% or three slides with width: 33.33%.
Here's a demonstration of what I'm talking about: https://codesandbox.io/s/embla-carousel-loop-true-xgg95
It's actually easy to fix by setting the width to 49.99 % but as this is only a workaround, I wanted to report the issue.
Once again, thanks for your efforts!
Would be helpful to animate some elements inside the slide when is selected.
Hi @davidcetinkaya - May I know if there's a future plan for RTL support or if there is a workaround for now, please let me know. Thank you!
// accepts an axis option with 'x' and 'y' as possible values
const options = { axis: 'y' }
const embla = EmblaCarousel(emblaNode, options)
To all guys sending me private emails instead of opening an issue about this.
Hi David,
I noticed that in 2.4.0 you introduced the containScroll option, which allows me to use that in combination with slidesToScroll to display multiple slides at once. I was wondering if you had any recommendations on how to make the carousel responsive when used in this way.
For example, I want to show a different number of slides depending on the size of the window...
One idea I had was to detect changes to the windows size and then fire the changeOptions() method to update the slidesToScroll option.
Is that the right approach, or would you recommend something else?
Add all contributors to README to show appreciation for their great efforts ๐ช๐ปโญ๏ธ!
Embla only seems to work after the page is refreshed in some way such as a resize.
When the page loads at first, embla appears to be unresponsive, as if it does not exist.
I noticed the 'is-dragging' classes are being applied meaning embla is loaded and working but the transforms don't get applied until I resize the page.
In hindsight, it might be a vue issue, but I wanted to get your thoughts first before filing a vue issue.
Hi! I ran into a nasty bug with unnecessary scroll under certain conditions:
loop
: true
draggable
: false
slidesToScroll
: more than 1 (for example 4)slidesToScroll
but not completely divisible by slidesToScroll
(for example 5)In this case after initial load when we scroll to next and try to click link on the first slide we see unnecessary scroll.
Here's a demonstration of what I'm talking about: https://codesandbox.io/s/embla-carousel-containscroll-mec6x
As I figured out it is because click on the link fires focus
event and Embla considers that this is not the current slide and does scroll.
Hello, i have a problem with checking if element exists and initializing the Embla carousel.
I want to do something like: If element exists on the page, then initialize it with Embla carousel, because when i do it without checking the element first, then on another subpage, i get an error saying "Error: No root element provided ๐ข"
My code looks like that:
import EmblaCarousel from 'embla-carousel';
const testimonials = document.querySelector('.embla__testimonials');
const checkTestimonials = () => {
return EmblaCarousel(testimonials, {
align: 'center',
containerSelector: '*',
slidesToScroll: 1,
containScroll: false,
draggable: true,
dragFree: false,
loop: false,
speed: 10,
startIndex: 0,
selectedClass: 'is-selected',
draggableClass: 'is-draggable',
draggingClass: 'is-dragging',
});
}
if (testimonials.length) {
checkTestimonials();
}
The code above doesn't work, it checks correctly for the element, but does not initialize the carousel, and i need to initialize it always after checking the element first :/
Does anyone know how i can solve it?
Thanks!
Wrong option name is showing in README:
const options = {
container: '*' // Should be containerSelector: '*'
}
const options = {
containerSelector: '*'
}
// Embla should fire an event when the carousel has stopped scrolling.
embla.on('settle', () => {
console.log('The carousel has stopped scrolling.')
})
@horgen for this feature request.
Create an Embla Carousel Vue wrapper - A component that allows Vue users to consume Embla Carousel.
Released with v7.0.0-rc05. Documentation will be released with stable v7 but until then you can check out usage instructions below:
Start by installing the Embla Carousel npm package and add it to your dependencies.
npm install embla-carousel-vue --save
Embla Carousel provides the handy emblaCarouselVue
function for seamless integration with Vue. A minimal setup requires an overflow wrapper and a scroll container. Start by adding the following structure to your carousel:
<template>
<div class="embla" ref="emblaNode">
<div class="embla__container">
<div class="embla__slide">Slide 1</div>
<div class="embla__slide">Slide 2</div>
<div class="embla__slide">Slide 3</div>
</div>
</div>
</template>
<script>
import emblaCarouselVue from 'embla-carousel-vue'
export default {
setup() {
const [emblaNode] = emblaCarouselVue()
return { emblaNode }
},
}
</script>
The emblaCarouselVue
function gives us an emblaNode to attach to our wrapping element with the classname embla
, which is needed to cover the scroll overflow. The element with the container
classname is the scroll body that scrolls the slides. Continue by adding the following CSS to these elements:
<style scoped>
.embla {
overflow: hidden;
}
.embla__container {
display: flex;
}
.embla__slide {
position: relative;
flex: 0 0 100%;
}
</style>
The emblaCarouselVue
function takes the Embla Carousel options as the first argument. Additionally, you can access the API with an onMounted
like demonstrated below:
<template>
<div class="embla" ref="emblaNode">
<div class="embla__container">
<div class="embla__slide">Slide 1</div>
<div class="embla__slide">Slide 2</div>
<div class="embla__slide">Slide 3</div>
</div>
</div>
</template>
<script>
import { onMounted } from 'vue'
import emblaCarouselVue from 'embla-carousel-vue'
export default {
setup() {
const [emblaNode, emblaApi] = emblaCarouselVue({ loop: false })
onMounted(() => {
if (emblaApi.value) {
// Embla API is ready
}
})
return { emblaNode, emblaApi }
},
}
</script>
The emblaCarouselVue
function also accepts plugins as the second argument. Note that plugins need to be passed in an array like so:
<template>
<div class="embla" ref="emblaNode">
<div class="embla__container">
<div class="embla__slide">Slide 1</div>
<div class="embla__slide">Slide 2</div>
<div class="embla__slide">Slide 3</div>
</div>
</div>
</template>
<script>
import emblaCarouselVue from 'embla-carousel-vue'
import Autoplay from 'embla-carousel-autoplay'
export default {
setup() {
const [emblaNode] = emblaCarouselVue({ loop: false }, [Autoplay()])
return { emblaNode }
},
}
</script>
Congratulations! You just created your first Embla Carousel component.
Hello, i have a problem with initializing Embla carousel after 992px and destroying it below that value.
My js code:
const initBrandsEmbla = () => {
const brandsEmbla = document.querySelector('.embla__brands');
if (brandsEmbla !== null) {
const settings = {
draggable: false,
loop: true,
align: 'start',
startIndex: 1,
};
const embla = EmblaCarousel(brandsEmbla, settings);
const autoPlayer = autoPlay(embla, 1000);
const checkEmbla = () => {
if (window.matchMedia('(max-width: 992px)').matches) {
// I want to reinitialize it here
// something like embla.recover() would be helpful
autoPlayer.play();
} else {
embla.destroy();
}
}
checkEmbla();
// On embla resize, check the width and initialize carousel, or destroy it
embla.on('resize', () => {
checkEmbla();
});
}
}
window.addEventListener('load', () => {
initBrandsEmbla();
});
The problem is: it works when i load the window that has width lower than 992, it also works as intended (the carousel stops) when i resize that window above 992px, but when i go back below 992px, the carousel doesn't recover or reinitialize no matter what i do.
I tried many things, but nothing worked so far.
When i try something like that:
const initBrandsEmbla = () => {
const brandsEmbla = document.querySelector('.embla__brands');
if (brandsEmbla !== null) {
const settings = {
draggable: false,
loop: true,
align: 'start',
startIndex: 1,
};
let embla;
let autoPlayer;
const checkEmbla = () => {
if (window.matchMedia('(max-width: 992px)').matches) {
// I want to reinitialize it here
embla = EmblaCarousel(brandsEmbla, settings);
autoPlayer = autoPlay(embla, 1000).play();
} else {
embla.destroy();
}
}
checkEmbla();
// On embla resize, check the width and initialize carousel, or destroy it
window.addEventListener('resize', () => {
checkEmbla();
});
}
}
window.addEventListener('load', () => {
initBrandsEmbla();
});
Then it doesn't stop on the desktop, i've ran out of ideas :/
Do you know the solution by any chance?
I tried to test the slider but seems you did not published it to npm.
Would be nice to have the slider accessibile, maybe with custom labels (i18n).
The API methods canScrollNext()
and canScrollPrev()
always return false when loop: true
. It should be the opposite. They should always return true for both these methods when loop: true
.
A browser support list
is missing. It should be added to the README ASAP.
Thanks @ehellman for reporting this!
It's would be good if you add autoplay option too.
Thanks for this awesome slider !
Create an Embla Carousel Angular npm package - A wrapper component that allows Angular users to consume Embla Carousel like the React implementation.
Please react with --> ๐ if you want this to be implemented.
Hey! Let me first say, loving the library so far. Very easy to get a really nice carousel with 0 config :) So thanks for the nice defaults.
I just got this error reported in my error tracking, and it leads to embla-carousel/lib/index
.
Environment:
Chrome Mobile Version 77.0.3865
Android Version 8.0.0
TypeError: Cannot read property '0' of undefined
at ./node_modules/embla-carousel/lib/index.js(read:1589:46)
at ./node_modules/embla-carousel/lib/index.js(apply:1417:29)
at ./node_modules/logrocket/dist/build.umd.js(HTMLElement.o:2857:31)
So that's
./node_modules/embla-carousel/lib/index.js in read on line 1589:46
1586 | function read(evt, axis) {
1587 | var isMouse = state.isMouse;
1588 | var c = coords[axis];
1589 | var value = isMouse ? evt[c] : evt.touches[0][c];
1590 | return pointValue.setNumber(value);
1591 | }
1592 | ย
1593 | function down(evt) {
1594 | state.isMouse = !!evt.type.match(/mouse/);
./node_modules/embla-carousel/lib/index.js in apply on line 1417:29
1413 | var resist = !params.loop && reachedLimit ? 2 : 1;
1414 | target.addNumber(diff / resist);
1415 | evt.preventDefault();
1416 | } else {
1417 | var X = pointer.read(evt, 'x').get();
1418 | var Y = pointer.read(evt, 'y').get();
1419 | var diffX = Math.abs(X - startX.get());
1420 | var diffY = Math.abs(Y - startY.get());
1421 | state.preventScroll = diffX > diffY;
1422 | if (!state.preventScroll && !state.preventClick) up();
If you need some copy and paste friendly text.
Not really sure how to go from here! I was going to default value to 0 if evt.touches is not defined although that is just a hack :)
Thanks!
Hi,
Sorry, I am puzzled, but where is CSS code located in your project? E.g., I see that, for example, <style id="/src/css/radio.css:-css" type="text/css">.radio {...
is generated by your script, but I could not locate that in the source code. To be honest, I really like your project, but your code has almost 0 comments, the overall structure is quite confusing, therefore I am not able to do anything with your code (help with adding new features, etc). Thanks.
When using touchpad on an MacBook, you can use two-finger gestures for scrolling. As far as I understand, these values are available through "wheel" events in javascript.
It would be very nice to have support for horizontal scrolling using touchpad in Embla Carousel if possible.
Here is a fiddle that demonstrates the values received: https://jsfiddle.net/64p5r459/2/
As already mentioned in #22, I think it would be desirable to make the threshold at which the slider starts to react to the hand/cursor movement configurable.
That might look like this:
const embla = EmblaCarousel(emblaNode, {
dragThreshold: 5,
...
})
With the value being the amount of pixels the hand/cursor has to move until the silder will follow its movement.
Apparently there already is a drag threshold already built in and I'm sure that the default value will be enough for most devs but depending on what kind of device the slider is used on, it could be quite useful (I'm thinking mostly about touch screens here).
Jeff
The discussion in this issue has resulted in a decision to implement the following API method:
// get scroll progress
// returns a number representing % scrolled of scrollable distance.
// 0 is start position and 1 is end position
embla.scrollProgress()
embla.on('scroll', () => {
console.log(`Current progress is: ${embla.scrollProgress() * 100}%`)
}
See example usage.
@xiel for discussing this and sharing your thoughts and ideas.
@welteki for the nice demonstration.
I actually see that you're currently tracking a similar request in the react version. https://github.com/davidcetinkaya/embla-carousel-react/issues/10
My issue is comparable, but just in a vanilla environment where I am encountering image load issues not correctly drawing the slideshow/allowing me to access it. However doing a simple browser resize appears to resolve it, so if there were an event on the api where we could refresh the slideshow init (similar to the settimeout fix on flickity for anyone familiar with forcing an update on that slideshow...). I realize in an ideal world we wouldn't need these functions and the slideshows would init perfectly but that isn't the case for me.
I'm getting around this by preloading all my images in the background before mounting the slideshow + a combination of index switching but it's not terribly elegant, it does however work. The usecase is a little harder to replicate because I am also creating multiple slideshows on the page via color variant selects (for an ecom experience)
It is an edge case:
When you right click while dragging and open the context menu and then release both mouse buttons the mouseup is not being triggered so you keep dragging till you click forcing a mousedown/up.
I can use CSS to quickly change Embla so that instead of showing a single slide, it shows more (e.g. width:25%
), however when using scrollNext
and scrollPrev
, it still scrolls by 100%, for the full number of slides.
This means that for several clicks on next/prev button, there are no slides showing. I really like Embla and don't want to have to implement a different solution, but I have no idea where to even start changing the TS to make it perform as expected. Do you have any plans to update to allow the functionality I'm after?
Hi, I found your lib after looking at many carousel libraries. I like yours because it's clean and light weight.
I've made an Angular wrapper around it and it works for static content. But I can't seem to get it working with content that's dynamically generated from an api request. Using the ngFor loop to generate the slides Embla returns empty node array and nothing works.
I guess it's only for static content? or am I doing something wrong?
Possible / how to do something like this?
import EmblaCarousel from 'embla-carousel';
var elements = document.querySelectorAll("li.carouselparent");
for (var i= 0; i < elements.length; i++) {
const emblaNode = document.querySelector('.embla');
const options = { loop: true };
const embla = EmblaCarousel(emblaNode, options);
}
Hello,
when trying to create an automated slideshow with two slides and loop: true
, I stumbled upon an interesting edge case. I used setInterval()
to call scrollNext()
every few seconds. After I switched tabs and came back later (one or two minutes is enough), the embla carousel was blank and the source code had a weird transform value:
<div class="embla__container" style="transform: translate3d(-5.72608e+10%, 0px, 0px);">
It can be reproduced here: https://codesandbox.io/s/embla-carousel-align-start-vllyp
I think this only happens in quite specific conditions - e.g. I was only able to reproduce this with two slides. I worked around the problem by using the Page Visibility API and only calling scrollNext()
if the page is visible, but I thought it might be worth reporting the issue.
Hi @davidjerleke!
Embla currently has ways to remove the leading and trailing blanks from the Slider (containsScroll: true
), but still allow for excessive scrolling. It would be interesting to add an option to block this scroll and stop it when it reaches the end or beginning of the Slider. I would like to know what you think of this idea
Below is the example of the "excessive scroll" I am referring to
Hi ! First of all, thanks for this great library.
I am actually working on an infinite looping carousel with dynamic content (meaning various slides number). I ran into a bothering problem when it comes to having a carousel containing less slides than needed to properly loop without layout breaking.
If the number of slides times the width of a slide is inferior to the width of the container plus half the width of a slide, embla does not manage to find a nice solution to display items.
It should imho, either not init embla and leave the layout as it is or clone items to fit container's width.
You can see what I mean with the examples below :
Broken carousel : https://codesandbox.io/s/embla-carousel-loop-ew22g
Carousel "fixed" that does not fit the requirements to init : https://codesandbox.io/s/embla-carousel-loop-scs24 <-- Even with that it sometimes has a little jump effect on scroll next
I created a carousel and added my custom clickable elements inside. The problem is when I drag the carousel and then I mouseup, click event is triggered which is not the desired action.
Currently I am implementing Embla Carousel in a project where we have multiple slides in the viewport at once, this basically means 2.5 slides are always visible.
When we containScroll and align start the items the snappoints for slide 4 and 5 are the same so it feels there should be one less snappoint for these settings.
I've setup a sandbox forked from one of the other issues to explain this issue: https://codesandbox.io/s/embla-carousel-loop-false-xdpsb?fontsize=14
When I have 2 slides, the carousel works as expected.
If I have just 1 slide the slide does not show and it is with left: 100%
.
I found that the reason is the option loop: true
.
Since we are populating the slides dynamically, can the lib disable the loop if the slide is just one?
// adds to current scroll progress
// accepts a number from 0 - 1 representing % to add to the current scroll progress
// scroll to target is smooth
embla.scrollBy(0.5)
As discussed here and here in issue #21.
@xiel for this feature request.
Hi @davidcetinkaya - thanks for your work on this - love your vision for a lightweight and flexible carousel!
I'm working on a project where it would be useful to specify the align
option as a percentage from the left edge (let me know if helpful to have a visual / example of my use case). Is this something you could see being added to Embla?
Hi, how can I fit the first item to the left when I have slides with fixed sizes?
I'm using the {align: "start"}, but I can't make it work, I'm trying to make it responsive putting the slides from the Left, maybe grouping cells? Thanks in advance.
here is the codepen https://codesandbox.io/s/embla-carousel-dragfree-k1ro4
Love the carousel and was able to get it working right away. The one thing that I need that it doesn't provide is a way to have a scroll option for single/multiple slides and a fade option to handle next/previous transition when it's just showing a single slide at a time.
Is that something that has been requested before?
Hello, when users swipe hard enough it looks like its skipping other slides, how can we limit it to only one slide per swipe? slidesToScroll: 1
doesn't seem to do anything
const wrap = document.querySelector('.js-home-slider');
const viewPort = wrap.querySelector('.js-home-slider-viewport');
const dots = wrap.querySelector('.js-home-slider-dots');
const homesliderStart = EmblaCarousel(viewPort, {
loop: true,
draggable: true,
dragFree: false,
speed: 10,
startIndex: 0,
slidesToScroll: 1,
});
Hello again, I was checking the example on CodeSandbox.
I wanted to customize the example to hide the "Previous" button when the first slide is shown and if the "loop" mode is off.
I have seen I can grab the current index position with selectedIndex()
and the total number of slides using groupedIndexes()
but it's not very obvious and the concept of "grouped slides" is a bit confusing.
Maybe embla
object could expose some convenient methods or properties like isFirstSlide()
, isLastSlide()
, hasPreviousSlide()
and hasNextSlide()
to make the task more convenient?
Or maybe there is a better approach, I may be missing something.
Thank you for your attention!
Hello, is it possible to have an option of selecting another css unit for the transform animation?
I mean: .embla__container is using translate3d(%, 0, 0) for the animations, but from what i've tested on my website, the performance would be better if i used translate3d(px, 0, 0) instead of % value.
Would you consider adding this feature to the Embla Carousel?
Thank you.
Hello again David @davidjerleke,
As mentioned in Best of JavaScript issue (michaelrambeau/bestofjs#237), I think you could improve the documentation by providing a full example of markup and JavaScript code.
You could use something like https://codepen.io and embed the "pen" directly in the demo page.
I'm asking because it took me some time to realize that I have to bind by myself the "Next" and "Previous" buttons to Embla methods.
Thank you for your attention!
Hi guys....
I'm just a rookie dev... in fact, I'm just code my portfolio with plane html, css and javascript and well, I thought this can be more easy to implement.
I just wonder if there anyway to use this without typescript or anyway more simple?
It would be great if the dragStart event would not fire when the mouse goes down but rather when the cursor actually starts moving. Currently, dragStart and dragEnd will be fired if I only click/touch the slider without moving the cursor.
This would be useful to me because I am currently listening to both click events on items and dragStart/dragEnd Events and change styles accordingly. This leads to the item click having weird effects as it first transitions to the "drag" styles and back before the click event is fired.
I am currently using embla-carousel-react
but I think this is something that concerns all variants of embla.
Let me know if you need an example.
Great Work by the way! ๐
It would be great to be able to use the left/right arrow keys to navigate slides by default when the carousel is in focus.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.