简体   繁体   中英

Pure JS Carousel bug

I'm exercising and I've tried to write my own carousel on pure JS.

JSFiddle

window.onload = function () {

    var slider = document.getElementById('slide-list');
    var left = document.getElementById('rewind');
    var right = document.getElementById('forward');

    var clickDisabled = false;

    function spin() {
        slider.firstChild.style.marginLeft = -800 + 'px';
        setTimeout(function () {
            slider.appendChild(slider.removeChild(slider.firstChild));
            slider.lastChild.style.marginLeft = '0px';
        }, 2000);
    }

    var slideShow = setInterval(spin, 4000);

    left.onclick = function () {
        if (clickDisabled) {return;}
        else {
            clickDisabled = true;
            clearInterval(slideShow);
            slider.lastChild.style.marginLeft = -800 + 'px';
            slider.insertBefore(slider.lastChild, slider.firstChild);
            // the crutch - try to unwrap content from timeout and see that image changes instantly, with no transition
            setTimeout(function () { slider.firstChild.style.marginLeft = '0px'; }, 1);
            slideShow = setInterval(spin, 4000);
            setTimeout(function() {clickDisabled = false;}, 2000);
        }
    }

    right.onclick = function () {
        // fix of fast sliding after multiple clicks
        if (clickDisabled) {return;}
        else {
            clickDisabled = true;
            clearInterval(slideShow);
            spin();
            slideShow = setInterval(spin, 4000);
            setTimeout(function() {clickDisabled = false;}, 2000);
        }
    }
}

The idea is that I have a list with carousel slides and I rearrange these slides with setInterval. Smooth sliding transition is achieved by means of having transititon in CSS and changing the slide's margin - hence the transition makes it slide smoothly.

Auto-spinning works ok and makes no problem, the thing is about changing slides manually, with left and right buttons. The problem is that it won't work without one very strange (to me) crutch. It's marked in the code(line 28). Seems like something is preventing the transition to finish, and without that crutch images change with no transition. I also would be very grateful for comments on my idea of temporary blocking onclick events for buttons to prevent mess and instant sliding if they're clicked multiple times. Maybe it's a bad way? What way could be better?

you could use this image slider I built... though it only works with modern browsers.

http://codepen.io/team/moderndeveloper/pen/MKgqzq

/* global Modernizr */

if (!Object.assign) {
  Object.defineProperty(Object, 'assign', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert first argument to object');
      }

      var to = Object(target);
      for (var i = 1; i < arguments.length; i++) {
        var nextSource = arguments[i];
        if (nextSource === undefined || nextSource === null) {
          continue;
        }
        nextSource = Object(nextSource);

        var keysArray = Object.keys(nextSource);
        for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
          var nextKey = keysArray[nextIndex];
          var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
          if (desc !== undefined && desc.enumerable) {
            to[nextKey] = nextSource[nextKey];
          }
        }
      }
      return to;
    }
  });
}

(function(window, document, Modernizr) {
  "use strict";

  var d = document;
  var transform = Modernizr.prefixed('transform');

  function ImageSliderIndicators(imageSlider, options) {
    this.imageSlider = imageSlider;
    this.options = Object.assign({}, ImageSliderIndicators.DEFAULTS, options || {});
    this.el = d.querySelector('.' + this.options.indicatorsClass);
    this.indicators = [].slice.call(d.querySelectorAll('.' + this.options.indicatorClass));

    this.imageSlider.el.addEventListener('positionChanged', this.onPositionChanged.bind(this));
    this.el.addEventListener('click', this.onIndicatorClick.bind(this), false);
    this.onPositionChanged();
  }

  ImageSliderIndicators.DEFAULTS = {
    indicatorsClass: 'ImageSlider-indicators',
    indicatorClass: 'ImageSlider-indicator',
    indicatorActiveClass: 'ImageSlider-indicator--is-active'
  };

  ImageSliderIndicators.prototype.onIndicatorClick = function onIndicatorClick(event) {
    var position = this.indicators.indexOf(event.target);
    if (position !== -1) {
      this.imageSlider.goto(position);
    }
  };

  ImageSliderIndicators.prototype.onPositionChanged = function onPositionChanged() {
    var self = this;
    this.indicators.forEach(function(element, index) {
      var action = index === self.imageSlider.position ? 'add' : 'remove';
      element.classList[action](self.options.indicatorActiveClass);
    });
  };

  function ImageSlider(options) {
    this.options = Object.assign({}, ImageSlider.DEFAULTS, options || {});
    this.position = 0;
    this.el = d.querySelector('.' + this.options.imageSliderClass);
    this.items = d.querySelector('.' + this.options.itemsClass);
    this.itemCount = d.querySelectorAll('.' + this.options.itemClass).length;
    this.scroller = d.querySelector('.' + this.options.scrollerClass);
    this.previousButton = d.querySelector('.' + this.options.previousButtonClass);
    this.nextButton = d.querySelector('.' + this.options.nextButtonClass);
    this.indicators = new ImageSliderIndicators(this, this.options.indicators);

    window.addEventListener('resize', this.render.bind(this));
    this.nextButton && this.nextButton.addEventListener('click', this.next.bind(this));
    this.previousButton && this.previousButton.addEventListener('click', this.previous.bind(this));
  }

  ImageSlider.DEFAULTS = {
    imageSliderClass: 'ImageSlider',
    itemsClass: 'ImageSlider-items',
    itemClass: 'ImageSlider-item',
    scrollerClass: 'ImageSlider-scroller',
    previousButtonClass: 'js-ImageSlider-button--previous',
    nextButtonClass: 'js-ImageSlider-button--next'
  };

  ImageSlider.prototype.render = function render() {
    this.items.style[transform] = 'translate3d(' + (-this.position * this.items.offsetWidth) + 'px,0,0)';
  };

  ImageSlider.prototype.goto = function goto(position) {
    var event = d.createEvent('Event');
    event.initEvent('positionChanged', true, true);
    this.position = position;
    this.el.dispatchEvent(event);
    this.render();
  };

  ImageSlider.prototype.previous = function previous() {
    this.goto((this.position + (this.itemCount - 1)) % this.itemCount);
  };

  ImageSlider.prototype.next = function next() {
    this.goto((this.position + 1) % this.itemCount);
  };

  window.ImageSlider = ImageSlider;

}).call(this, window, window.document, Modernizr);

new ImageSlider();

To keep it simple... the image, the css and the javascript are all rendering at the exact same time. As such the transition can't take place because by the time the transition is being run, the image already has the value you are "Changing it" to.

Its rendering is taking place after the value has added and thus no transition. Using a setTimeout is fairly standard in this situation. However you should realize that if someone is on a slow internet connection, a 1/1000th of a second timeout may not always be enough.

The correct solution is instead of changing your style with javascript, to create a class that overrides the default, and use javascript to switch your class.

Hopefully this is ACTUALLY an answer to your question and not just me pointing you to use pre-existing solutions.

Personally though, I do exactly what you are doing as can be seen in my own carousel example:

/* set each image except first to not display */  
  document.querySelectorAll('.imgContainer div:not(:nth-child(1))').forEach(function(el) {
    el.style.display = 'none';
    el.style.opacity =  0;
  });

/* set onclick event for navigation dots */
  var dotty = document.querySelectorAll('ul li');
  for (var i = 0; i < dotty.length; i++) {
    dotty[i].addEventListener("click", contentChanger);
  }

/* rotate function */
  function contentChanger(evt) {
    var id = evt.target.id;
    x = evt.target.id;
    [].forEach.call(document.querySelectorAll('li'), function (el) {
      el.classList.remove('active');
    });
    evt.target.classList.add('active');
    document.querySelectorAll('.imgContainer div:not(:nth-child(' + id + '))').forEach(function(el) {
      el.style.display = 'none';
      el.style.opacity = '0';
    });
    document.querySelector('.imgContainer div:nth-child(' + id + ')').style.display = 'block';
    setTimeout(function(){
      document.querySelector('.imgContainer div:nth-child(' + id + ')').style.opacity =  1;
    }, 50);
  }

/* only required to automate rotation leave this code out if you want only a click navigation. */
  function clickIt(i) {
    document.querySelector('ul li:nth-child('+i+')').click();
  }
  var x = 0;
  setInterval(function(){
    x++;
    if (x > dotty.length) { x = 1; }
    clickIt(x);
  }, 5000);

https://codepen.io/billy-bucks/pen/RwKRyEx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM