How to join setInterval and .on("click") function together?

1.5k Views Asked by At

I have an extremely primitive slider using fadeIn, fadeOut and some control bullets.

https://jsfiddle.net/c2dsnr8v/1/

<div class="view">
  <ul class="list">
    <li class="frst">
      <img src="http://www.guessthelogo.com/wp-content/uploads/2012/11/random-dice.gif" />
    </li>
    <li class="scnd">
      <img src="https://avatars.yandex.net/get-music-content/a19fc9b4.a.1767585-1/200x200" />
    </li>
    <li class="thrd">
      <img src="http://randomacts.channel4.com/images/fb_logo.gif" />
    </li>
    <li class="frth">
      <img src="http://cs620120.vk.me/v620120530/93f0/k7U9HGQOBkw.jpg" />
    </li>
  </ul>
  <div class="ctrl">
    <div class="bullet one"></div>
    <div class="bullet two"></div>
    <div class="bullet three"></div>
    <div class="bullet four"></div>
  </div>
</div>

$(".list li:gt(0)").hide();

var int = setInterval(function(){
$('.list > :first-child')
.fadeOut()
.next()
.fadeIn()
.end()
.appendTo('.list');} ,3000);

$(".bullet").on("click", function(){
  clearInterval(int);
  $(".list li").fadeOut();

  var $this = $(this);

  if($this.hasClass("one")){
    $(".list li.frst").fadeIn();
  }else if($this.hasClass("two")){
    $(".list li.scnd").fadeIn();
  }else if($this.hasClass("three")){
    $(".list li.thrd").fadeIn();
  }else if($this.hasClass("four")){
    $(".list li.frth").fadeIn();
  }
})

I figured out how I can make pictures appear on clicking bullets (green squares), and the setInterval function here is clear enough. But when I tried to join these mechanisms together, I found out that I can only clear setInterval with a click. So once I use bullets, automatic rotating doesn't work anymore.

Is there any way to join the two together, using this code? For example, I click on a bullet, the picture stands still for 5 seconds, and then it keeps rotating further with the same speed?

I tried to include a new setInterval after each bullet click, but failed.

3

There are 3 best solutions below

1
On BEST ANSWER

Here is an updated snippet that removes the jumpiness. Basically the issue was in the ".appendTo('.list');", which reordered the list items and made it difficult to continue the rotation once a bullet was clicked. The code significantly changed to get around this, but it should be more efficient since the dom isn't being reordered every time through. Also, I removed a bunch of unnecessary classes and and am now using .index() to coordinate between the bullets and list items, as mentioned previously.

(function() {
  var start = function() {
      var active = $('.active'),
        next = active
        .removeClass('active')
        .fadeOut()
        .next();
      if (!next.length) {
        next = $('ul li:first-child');
      }
      next
        .addClass('active')
        .fadeIn()
        .end();
    },
    go = function() {
      return setInterval(start, 2000);
    },
    int = go();
  $(".bullet").on("click", function() {
    var currentIndex = $('div.bullet').index(this),
      currentLi = $('ul li').eq(currentIndex);
    clearInterval(int);
    $('.active').removeClass('active').fadeOut();
    currentLi.addClass('active').fadeIn();
    int = go();
  })
}());
.view {
  margin: 100px auto 0;
  width: 200px;
  position: relative;
}
ul {
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
}
ul li {
  position: absolute;
  top: 0;
  left: 0;
}
.ctrl {
  bottom: -215px;
  display: flex;
  justify-content: space-between;
  position: absolute;
  width: 100%;
}
.bullet {
  background-color: green;
  height: 10px;
  width: 10px;
  cursor: pointer;
}
<div class="view">
  <ul>
    <li class="active">
      <img src="http://www.guessthelogo.com/wp-content/uploads/2012/11/random-dice.gif" />
    </li>
    <li style="display:none;">
      <img src="https://avatars.yandex.net/get-music-content/a19fc9b4.a.1767585-1/200x200" />
    </li>
    <li style="display:none;">
      <img src="http://randomacts.channel4.com/images/fb_logo.gif" />
    </li>
    <li style="display:none;">
      <img src="http://cs620120.vk.me/v620120530/93f0/k7U9HGQOBkw.jpg" />
    </li>
  </ul>
  <div class="ctrl">
    <div class="bullet"></div>
    <div class="bullet"></div>
    <div class="bullet"></div>
    <div class="bullet"></div>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

1
On

You can use an immediate function to encapsulate your vars and functions and avoid calling setInterval twice:

(function() {
  $(".list li:gt(0)").hide();
  var start = function() {
      $('.list > :first-child')
        .fadeOut()
        .next()
        .fadeIn()
        .end()
        .appendTo('.list');
    },
    go = function(elem) {
      return setInterval(start, 2000);
    },
    int = go();
  $(".bullet").on("click", function() {
    clearInterval(int);
    $(".list li").fadeOut();
    var $this = $(this);
    if ($this.hasClass("one")) {
      $(".list li.frst").show();
    } else if ($this.hasClass("two")) {
      $(".list li.scnd").show();
    } else if ($this.hasClass("three")) {
      $(".list li.thrd").show();
    } else if ($this.hasClass("four")) {
      $(".list li.frth").show();
    };
    setTimeout(function() {
      int = go();
    }, 2000);
  })
}());

(function() {
  $(".list li:gt(0)").hide();
  var start = function() {
      $('.list > :first-child')
        .fadeOut()
        .next()
        .fadeIn()
        .end()
        .appendTo('.list');
    },
    go = function(elem) {
      return setInterval(start, 2000);
    },
    int = go();
  $(".bullet").on("click", function() {
    clearInterval(int);
    $(".list li").fadeOut();
    var $this = $(this);
    if ($this.hasClass("one")) {
      $(".list li.frst").show();
    } else if ($this.hasClass("two")) {
      $(".list li.scnd").show();
    } else if ($this.hasClass("three")) {
      $(".list li.thrd").show();
    } else if ($this.hasClass("four")) {
      $(".list li.frth").show();
    };
    setTimeout(function() {
      int = go();
    }, 2000);
  })
}());
.view {
  margin: 100px auto 0;
  width: 200px;
  position: relative;
}
.list {
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
}
.list li {
  position: absolute;
  top: 0;
  left: 0;
}
.ctrl {
  bottom: -215px;
  display: flex;
  justify-content: space-between;
  position: absolute;
  width: 100%;
}
.bullet {
  background-color: green;
  height: 10px;
  width: 10px;
  cursor: pointer;
}
<div class="view">
  <ul class="list">
    <li class="frst">
      <img src="http://www.guessthelogo.com/wp-content/uploads/2012/11/random-dice.gif" />
    </li>
    <li class="scnd">
      <img src="https://avatars.yandex.net/get-music-content/a19fc9b4.a.1767585-1/200x200" />
    </li>
    <li class="thrd">
      <img src="http://randomacts.channel4.com/images/fb_logo.gif" />
    </li>
    <li class="frth">
      <img src="http://cs620120.vk.me/v620120530/93f0/k7U9HGQOBkw.jpg" />
    </li>
  </ul>
  <div class="ctrl">
    <div class="bullet one"></div>
    <div class="bullet two"></div>
    <div class="bullet three"></div>
    <div class="bullet four"></div>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>

There is some initial "jumpiness" after clicking a bullet before it settles in that you might have to tweak. Also, you could probably clean up some of your code by using jQuery's .index() method, if desired.

1
On

I think what you are looking for is setTimeout. setTimeout will execute a function once after a set amount of milliseconds. So in this example you can do:

//clear timeoutid incase you click a lot
clearTimeout(timeoutid);
timeoutid = setTimeout(function(){ 
  int = setInterval(function(){
    //List stuff
  },2000);
},3000);

So in this case the next fade wont happen for 5 seconds after the click, but the rotation will continue on a 2 second interval.

Example JSFiddle:
https://jsfiddle.net/u6sdb40j/1/