Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation and pagination positioning around slider (not over) #3736

Closed
1 of 3 tasks
aprat84 opened this issue Jul 29, 2020 · 14 comments
Closed
1 of 3 tasks

Navigation and pagination positioning around slider (not over) #3736

aprat84 opened this issue Jul 29, 2020 · 14 comments
Milestone

Comments

@aprat84
Copy link

aprat84 commented Jul 29, 2020

This is a (multiple allowed):

  • bug

  • enhancement

  • feature-discussion (RFC)

  • Swiper Version: 6.0.4

  • Platform/Target and Browser Versions: Ubuntu 18 // Google Chrome 84.0.4147.105

Issue

I wish I could move arrows and pagination/scrollbar outside the slider (not over). Now it's impossible to do it correctly and honour the vertical/horizontal direction.

  • swiper-container has overflow:hidden, so I can't position arrows/pagination/scrollbar elements outside it or they get hidden.
    I can't remove the overflow:hidden property from container or slides get visible outside the container...
  • xxx-horizontal and xxx-vertical classes are added to swiper-container, so even if I take the arrows/pagination/scrollbar outside the container, I'd be unable to know which direction is the slider configured with (yes, I could use the "all siblings css selector" ~, but I'd feel kinda dirty)

Possible solutions

  1. Add extra div around the swiper-wrapper, something like swiper-clip that has the overflow:hidden style.
<div class="swiper-container">
    <div class="swiper-clip">
        <div class="swiper-wrapper">
            <div class="swiper-slide">Slide 1</div>
            <div class="swiper-slide">Slide 2</div>
            ...
        </div>
    </div>
    <div class="swiper-pagination"></div>
    <div class="swiper-button-prev"></div>
    <div class="swiper-button-next"></div>
    <div class="swiper-scrollbar"></div>
</div>
  1. Search for wrapperClass hierarchically within the element, instead of searching only direct children. Use find method here (like it is used for prevEl, nextEl, pagination.el, ...):
    $wrapperEl = $el.children(`.${swiper.params.wrapperClass}`);

    This way I could create the swiper-clip element myself

Option 1 would be my prefered, though it would not be simple, and may cause a new major version.
Option 2 is very simple, just a patch.

@stratboy
Copy link

me too: I'd like to build a carousel (Multiple Slides Per View) with arrows at left and right outside the slideshow area. How to?

@vasilestefirta
Copy link

vasilestefirta commented Jul 29, 2020

I was able to get this working today. Here's the HTML layout:

<div class="relative -mx-6 px-6">
    <div class="swiper-container home-large-carousel w-full mt-2">
        <div class="swiper-wrapper">
            <div class="swiper-slide md:flex">
                ...
            </div>
            <div class="swiper-slide md:flex">
                ...
            </div>
            <div class="swiper-slide md:flex">
                ...
            </div>
        </div>

        <div class="carousel-pagination flex items-center justify-center mt-5"></div>
    </div>

    <div class="carousel-navigation-prev flex items-center absolute z-10 top-0 bottom-0 left-0 right-auto cursor-pointer" role="button" aria-label="Previous Video">
        {{-- SVG ICON --}}
    </div>

    <div class="carousel-navigation-next flex items-center absolute z-10 top-0 bottom-0 left-auto right-0 cursor-pointer" role="button" aria-label="Next Video">
        {{-- SVG ICON --}}
    </div>
</div>

And here's the JS (the snippet below also includes custom pagination):

// core version + navigation, pagination modules:
import Swiper, { Navigation, Pagination } from "swiper";

// configure Swiper to use modules
Swiper.use([Navigation, Pagination]);

// import Swiper styles
import "swiper/swiper-bundle.css";

/**
 * Create Swiper instances.
 */
const swiperCarousels = document.querySelectorAll(".swiper-container");

if (swiperCarousels.length) {
    swiperCarousels.forEach(carouselContainer => {
        const carouselPagination = carouselContainer.querySelector(
            ".carousel-pagination"
        );

        const carouselPrevButton =
            carouselContainer.querySelector(".carousel-navigation-prev") ||
            carouselContainer.parentElement.querySelector(
                ".carousel-navigation-prev" // <-- this is how you can have the Prev Button outside the swiper container
            );

        const carouselNextButton =
            carouselContainer.querySelector(".carousel-navigation-next") ||
            carouselContainer.parentElement.querySelector(
                ".carousel-navigation-next" // <-- this is how you can have the Next Button outside the swiper container
            );

        const swiperInstance = new Swiper(carouselContainer, {
            loop: true,

            pagination: {
                el: carouselPagination,
                type: "custom",
                renderCustom: renderCarouselPaginationBullets
            },

            navigation: {
                nextEl: carouselNextButton,
                prevEl: carouselPrevButton
            }
        });

        /**
         * Swiper's "clickable" option does not work with custom pagination,
         * so we have to manually make the pagination's bullets clickable.
         */
        carouselPagination.addEventListener("click", function(e) {
            const { slide } = e.target.dataset;

            if (slide) {
                swiperInstance.slideTo(slide);
            }
        });
    });
}

/**
 * Generate custom pagination bullets.
 *
 * @param {Swiper} swiper
 * @param {number} current
 * @param {number} total
 *
 * @return {string}
 */
function renderCarouselPaginationBullets(swiper, current, total) {
    const bullets = [];

    for (let index = 1; index <= total; index++) {
        const bullet = document.createElement("div");
        let className =
            "w-2 h-2 bg-gray-400 rounded-full mr-1 cursor-pointer";

        if (current === index) {
            // handle active bullet
        }

        bullet.className = className;
        bullet.setAttribute("data-slide", index);

        bullets.push(bullet);
    }

    return bullets.map(bullet => bullet.outerHTML).join(" ");
}

@dseid-v4
Copy link

Same for me, I'd like to position the slider nav outside the slides, too. The workarounds look promising. Maybe there could be integrated a native solution?

@stratboy
Copy link

stratboy commented Jul 30, 2020

In fact, just put them wherever you want and then reference them when initializing the Swiper. If you have many swipers, put them into containers with unique ids and then use the ids to reference the navigation buttons.

For example, here the swiper code in on the 'slideshow' external template, and it's on a content-section container with unique ID:

<div class="content-section slideshow-block carousel-block <?php echo $object_id ?> ">
  <?php get_template_part('template-parts/slideshow'); ?>

  <div class="carousel-swiper-navigation">
    <div class="carousel-slider-button-prev slider-arrow"><?php echo file_get_contents(get_img_url('freccia-slideshow.svg')); ?></div>
    <div class="carousel-slider-button-next slider-arrow"><?php echo file_get_contents(get_img_url('freccia-slideshow.svg')); ?></div>
  </div>
</div>

Then in my js code:

[...]
let object_id = slider_element.data('objectid');

  let options = {
    slidesPerView: 4,
    spaceBetween: 30,
    navigation: {
      nextEl: '.' + object_id + ' .carousel-slider-button-next',  // look here
      prevEl: '.' + object_id + ' .carousel-slider-button-prev', // and here
    }
    ,watchOverflow: true // disable con una sola slide
  }
[... and so on, then pass the options to swiper]

@aprat84
Copy link
Author

aprat84 commented Aug 3, 2020

If you put them outside swiper-container, you lose some CSS, cause arrows and pagination positioning depends on the slider orientation, and swiper adds swiper-container-horizontal and swiper-container-vertical classes to swiper-container.

IMHO, best workaround is option 2 in first message, if u are able to edit the code.
Permanent solution involves some type of "clipping container" between the wrapper and the container div's.

@stratboy
Copy link

stratboy commented Aug 3, 2020

Obviously, when you put things outside the swiper container, you put them into your responsibility. But It's a well made and though script, it's not too difficult to add some good css to make it as you need. For me it took just minutes.

I think it depends on what one needs and what his skills are.

Anyway, if the developer will add some new updates/utilities to Swiper to accomplish our needs, it's better, for sure. But don't be scared of my method: most of the times will do, fast and easy.

@aprat84
Copy link
Author

aprat84 commented Aug 3, 2020

I know, don't get me wrong, I could easily do my own CSS, but I'd prefer not, not just only to "not reinvent the wheel", but to prevent me to fix that CSS in case future releases changes.

My workaround was to edit the plugin code (cause I use it in a legacy project: no NPM, no ES6+), so that the plugin searches for the wrapper div in all children hierarchically, and inject my own swiper-clip div.

This is a pretty good and easy "fix" for the developers to do, which requires no new major version imho.

@valse
Copy link

valse commented Aug 4, 2020

Hi, how can I use outside navigation's element with the React component too?
At the moment I'm using CSS selector like:

navigation={{
   prevEl: ".my__slider .swiper-button-prev.icon-prev",
   nextEl: ".my__slider .swiper-button-next.icon-next",
}}

but if I have more then one of the same component, the pagination click will trigger all the swipers.

I tried using ref but with no success because the current element is null when Swiper is initialized.

@stratboy
Copy link

stratboy commented Aug 4, 2020

I don't use react. Anyway the same issue applies on non react environments. I think the easiest thing is to wrap the sliders in elements with unique ids and use the ids for selectors. Please see my example above.

@florianGierlichs
Copy link

@nolimits4web could you explain, how can I make custom navigation outside the container happen, using react?
Like @valse mentioned, using ref doesn't work, because the current element is null when Swiper is initialized.
thanks mate

@Simonbelete
Copy link

I achieved navigation around with margin and position

<div className="relative md:w-1/6">
      <Swiper
        spaceBetween={0}
        slidesPerView={1}
        navigation={{
          prevEl: navigationPrevRef.current,
          nextEl: navigationNextRef.current,
        }}
        modules={[Navigation]}
        style={{ marginLeft: '80px', marginRight: '80px', position: 'unset' }}
      >
        <SwiperSlide className="flex items-center justify-center">
          <div>
            <p>Slide 1</p>
            <p>Slide 1</p>
            <p>Slide 1</p>
            <p>Slide 1</p>
            <p>Slide 1</p>
            <p>Slide 1</p>
          </div>
        </SwiperSlide>
        <SwiperSlide className="flex items-center justify-center">
          <div>
            <p>Slide 2</p>
            <p>Slide 2</p>
            <p>Slide 2</p>
            <p>Slide 2</p>
            <p>Slide 2</p>
            <p>Slide 2</p>
          </div>
        </SwiperSlide>
        <div
          ref={navigationPrevRef}
          className="flex items-center absolute top-0 bottom-0 left-0 right-auto cursor-pointer"
        >
          {/* --- SVG ICON */}
        </div>
        <div
          ref={navigationNextRef}
          className="flex items-center absolute top-0 bottom-0 left-auto right-0 cursor-pointer"
        >
          {/* --- SVG ICON */}
        </div>
      </Swiper>
    </div>
```

@Crossmarx
Copy link

Crossmarx commented Apr 24, 2023

My solution for pagination is to allow overflow in y-direction add a padding at the bottom:
.swiper {
overflow-y: visible !important;
padding-bottom: 3vw !important;
}

@giakhanh22024558
Copy link

hi are there any solutions for this problem with CDN option

@Micelloplutarh
Copy link

Micelloplutarh commented Apr 28, 2024

Found a solution that simulates navigation outside the container, made the slides width 93%:
.swiper-container.swiper-slide img, .slider-item{
width: 93%;
},
and centered the swiper-slide containers with flexbox (added via classes).
<swiper-slide class=“flex flex-center”>

.flex{
display:flex;
}
.flex-center{
justify-content: center;
align-items: center;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests