Why is Bootstrap 4 carousel pause function not working when it's getting called from the event of an element inside of the carousel?

So I was trying to wrap my head around this issue, and I asked the question yesterday: Carousel('pause') isn't working on mobile devices

I found the symptom so I'm asking a new question with a new focus.

The question title is not telling everything, because it would be too large: This is only happening on phones, using mobile browsers, On desktop is totally fine.

Basically, if you have a Bootstrap 4 carousel, and you have any element that is inside of the div with the id of the carousel and you want to attach an event on that element (like a click) to pause the carousel, it wont work AT ALL, the slider keeps cycling.

I've tried click, mousedown, mouseup, I've tried setting the selector's click event on body, tried calling a click event on a selector that is outside to pause it, used parents, etc, and it wouldn't work.

Created a button that is right outside the carousel, attached a click event to it and it works. I'm using the latest BS (4.4.1)

This is my code:

Blade file:

<div id='slider'>
    <button class='pause-carousel' role='button'>Pause from outside</button>
    <div id='sme-directory-gallery' class='carousel slide' data-ride='carousel'>
        <button class='pause-carousel-2' role='button'>Pause from inside</button>
        <div class='carousel-inner'>
            @foreach($directoryInfo->videos as $index => $video)
                <div class='item carousel-item {{ $index === 0 ? 'active' : '' }}' data-slide-number='{{ $index }}'>
                    <div class='video-mask'></div>
                    <div class='d-flex justify-content-center'>
                        <div class='embed-responsive embed-responsive-21by9'>
                            <iframe class='embed-responsive-item player' src='{{ 'https://www.youtube.com/embed/' . substr($video->url, strrpos($video->url, 'v=') + 2) }}' allowfullscreen></iframe>
                        </div>
                    </div>
                </div>
            @endforeach

            @foreach($directoryInfo->images as $index => $image)
                <div class='item carousel-item {{ ($index + sizeof($directoryInfo->videos)) === 0 ? 'active' : '' }}' data-slide-number='{{ $index + sizeof($directoryInfo->videos) }}'>
                    <div class='d-flex justify-content-center'>
                        <img src='{{ asset('storage/' . $image->path) }}' class='img-fluid' alt='imagen'>
                    </div>
                </div>
            @endforeach

            <a class='carousel-control-prev' href='#sme-directory-gallery' role='button' data-slide='prev'>
                <div class='rounded-circle'>
                    <span class='carousel-control-prev-icon' aria-hidden='true'></span>
                    <span class='sr-only'>Previous</span>
                </div>
            </a>
            <a class='carousel-control-next' href='#sme-directory-gallery' role='button' data-slide='next'>
                <div class='rounded-circle'>
                    <span class='carousel-control-next-icon' aria-hidden='true'></span>
                    <span class='sr-only'>Next</span>
                </div>
            </a>
        </div>

        <ul class='carousel-indicators list-inline'>
            @foreach($directoryInfo->videos as $index => $video)
                <li class='list-inline-item my-2 {{ $index === 0 ? 'active' : '' }}'>
                    <a id='carousel-selector-{{ $index + sizeof($directoryInfo->videos) }}' class='sme-gallery-anchor' data-slide-to='{{ $index }}' data-target='#sme-directory-gallery'>
                        <img src='{{ 'https://img.youtube.com/vi/' . substr($video->url, strrpos($video->url, 'v=') + 2) . '/mqdefault.jpg' }}' class='img-fluid'>
                        <div class='text-white sme-gallery-middle-icon'>
                            <span class='fas fa-play'></span>
                        </div>
                    </a>
                </li>
            @endforeach

            @foreach($directoryInfo->images as $index => $image)
                <li class='list-inline-item {{ $index + sizeof($directoryInfo->videos) === 0 ? 'active' : '' }}'>
                    <a id='carousel-selector-{{ $index + sizeof($directoryInfo->videos) }}' data-slide-to='{{ $index + sizeof($directoryInfo->videos) }}' data-target='#sme-directory-gallery'>
                        <img src='{{ asset('storage/' . $image->path) }}' class='img-fluid'>
                    </a>
                </li>
            @endforeach
        </ul>
    </div>
</div>

Javascript:

$(document).ready(function () {
    $('.carousel-indicators').scrollLeft(0);
    $('#sme-directory-gallery').carousel();

    $('.pause-carousel').on('click', function () {
        $('#sme-directory-gallery').carousel('pause'); // it works
    });

    $('.pause-carousel-2').on('click', function () {
        $('#sme-directory-gallery').carousel('pause'); // it wont work
    });

    $('#sme-directory-gallery .video-mask').mouseup(function () {
        var iframe = $(this).closest('.item').find('iframe');
        var iframe_source = iframe.attr('src');
        if (iframe_source.includes('?rel=0')) {
            iframe_source = iframe_source.replace('?rel=0', '');
        }
        iframe_source = iframe_source + '?autoplay=1';
        iframe.attr('src', iframe_source);
        $(this).toggle();
        $('#sme-directory-gallery').carousel('pause');
    });

    $('#sme-directory-gallery').on('slide.bs.carousel', function (e) {
        $('#sme-directory-gallery').carousel('cycle');

        var iframeID = $(this).find('iframe').attr('id');

        if (iframeID != undefined) {
            callPlayer(iframeID, 'stopVideo');
        }

        $('.video-mask').show();
        $('#sme-directory-gallery').find('iframe').each(function (key, value) {
            var url = $(this).attr('src');
            if (url.includes('?autoplay')) {
                var new_url = url.substring(0, url.indexOf('?'));
                $(this).attr('src', new_url);
            }
        });

        setTimeout(() => {
            var scrollTo = $('.list-inline-item.active').position().left;
            if ($('.list-inline-item.active').index() > 0) {
                scrollTo = $('.list-inline-item.active').prev('.list-inline-item').position().left;
            }
            var finalOrFirst = 0;
            if (e.direction === 'right') {
                if ($('.list-inline-item.active').is(':last-child')) {
                    finalOrFirst = 99999;
                }
            } else {
                if ($('.list-inline-item.active').is(':first-child')) {
                    finalOrFirst = -99999;
                }
            }

            var currentScroll = $('.carousel-indicators').scrollLeft();
            var scrollResult = currentScroll + scrollTo + finalOrFirst;
            $('.carousel-indicators').animate({scrollLeft: scrollResult}, 500);
        }, 10);
    });
});

What exactly is happening here, how do I fix it?

Thanks in advance.

Upvotes: 1

Views: 4060

Answers (4)

PiemP
PiemP

Reputation: 141

I have a problem similar to this one: in some case the code doesn't work in some case works. The only difference I have found is about the presence of the class "pointer-event" in the tag of the carousel element.

To solve this problem I have use a boolean variable with the event "slide.bs.carousel" to avoid carousel to slide when paused.

HTML CODE

<section id="slideshow" class="carousel slide">
  <div class="carousel-inner" id="images" >
    <div class="carousel-item " >
      <img src="..." class="d-block w-100" alt='bla bla bla' >
    </div>
    <div class="carousel-item " >
      <img src="..." class="d-block w-100" alt='bla bla bla' >
    </div>
    <div class="carousel-item " >
      <img src="..." class="d-block w-100" alt='bla bla bla' >
    </div>
  </div>
  <a class="carousel-control-pause" href="#slideshow" role="button"  >
    <span class="pause-icon">
        ||
    </span>
    <span class="play-icon d-none">
        |>
    </span>
  </a>
  <a class="carousel-control-prev" href="#slideshow" role="button" data-slide="prev"  >
    <--
  </a>
  <a class="carousel-control-next" href="#slideshow" role="button" data-slide="next"  >
    -->
  </a>
</section>

JAVASCRIPT CODE

$(document).ready(function(){

  var carousel = $('#slideshow').carousel({ 
    interval: 5000,
    ride: true 
  });
  var carousel_paused = false;

  //on init the carousel is paused
  $('#slideshow .carousel-control-pause').on('click', function(e){
    e.preventDefault();

    var hideclass= "d-none";
    var pause= $(this).children(".pause-icon");
    if( $(pause).is(":hidden"))
    {
      //play
      $(pause).removeClass(hideclass);
      $(this).children(".play-icon").addClass(hideclass);
      carousel.carousel('cycle');
      carousel_paused = false;
    }
    else
    {
      //pause
      $(pause).addClass(hideclass);
      $(this).children(".play-icon").removeClass(hideclass);
      carousel.carousel('pause');
      carousel_paused = true;
    }
  });

  carousel.on('slide.bs.carousel', function (e) {
    console.log("slide pre");
    //prevent slide if paused
    if(carousel_paused)
      e.preventDefault();
  });

  carousel.on('slid.bs.carousel', function (e) {
    console.log("slide done");
    
  });

});

The code is for example and is not tested in mobile.

Upvotes: 0

ariferol01
ariferol01

Reputation: 249

If you use .carousel() as a varaible it will work. var slider = $('#element').carousel(); And you can use in all events. slider.carousel('pause'); , for starting again: slider.carousel({'pause': false});

When you use multiple element click events:

$('#btn1, #btn2').click(function(){
slider.carousel('pause');
});

In an another event (hover):

$('#yourSlider').hover(function(){
  slider.carousel('pause');
  }, function(){
  slider.carousel({'pause': false});
});

If you don't assign it to a variable, the carousel will be restarted within each event.

I added an examp (it works):

$(function(){
var slider = $('#carouselExampleSlidesOnly').carousel({
  interval: 1000
});
$('#pause').click(function(){
slider.carousel('pause');
});

$('#start').click(function(){
slider.carousel({'pause': false});
});

});
.carousel-item{
padding-top: 45px;
text-align: center;
height:150px;
width:100%;
color:#fff;
background-color: #666;
font-size: 2rem;
font-weight: 700;
}
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>


<div class="container p-4">
<button id="pause" class="btn btn-sm btn-warning">Pause</button>
<button id="start" class="ml-2 btn btn-sm btn-primary">Start</button>
</div>

<div id="carouselExampleSlidesOnly" class="carousel slide" data-ride="carousel" style="width: 90%; marign:auto">
  <div class="carousel-inner">
    <div class="carousel-item active">
      Slide 1
    </div>
    <div class="carousel-item">
      Slide 2
    </div>
    <div class="carousel-item">
      Slide 3
    </div>
  </div>
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>

<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

Upvotes: 1

esoteric
esoteric

Reputation: 125

Set the pause to false and that will take care of the issue

$('.carousel').carousel({
    pause: "false"
});

you can see an example here: https://jsfiddle.net/KyleMit/XFcSv/

Upvotes: 1

Zeeshan Eqbal
Zeeshan Eqbal

Reputation: 260

Try this to catch the issue:

Put console.log or alert() to check if event is triggered or not.

    $('.pause-carousel-2').on('click', function () {
        console.log('Button Clicked');
        $('#sme-directory-gallery').carousel('pause'); // it wont work
    });

Case 1:

If "Button Click" is not printed in console, then you may try:

$('body').on('click','.pause-carousel-2',function(){ ...

Case 2:

If "Button Click" is printed, then there is a workaround for this issue, which you can try.

Use Trigger

    $('.pause-carousel-2').on('click', function () {
        $('.pause-carousel').trigger("click");
    });

and hide the Pause button which is outside

Please share your result, will try to fix your issue.

Upvotes: 1

Related Questions