Remove jQuery dependency on simple text rotation

At the moment I am loading a whole jQuery library for something that is really rather simple. It relates through a list of span's which each contain different text, so that the message is different each time.

Here is the code that I have:

(function($){
    $.fn.extend({ 
        rotaterator: function(options) {

            var defaults = {
                fadeSpeed: 500,
                pauseSpeed: 500,
                child:null
            };

            var options = $.extend(defaults, options);

            return this.each(function() {
                  var o =options;
                  var obj = $(this);                
                  var items = $(obj.children(), obj);
                  items.each(function() {$(this).hide();})
                  if(!o.child){var next = $(obj).children(':first');
                  }else{var next = o.child;
                  }
                  $(next).fadeIn(o.fadeSpeed, function() {
                        $(next).delay(o.pauseSpeed).fadeOut(o.fadeSpeed, function() {
                            var next = $(this).next();
                            if (next.length == 0){
                                    next = $(obj).children(':first');
                            }
                            $(obj).rotaterator({child : next, fadeSpeed : o.fadeSpeed, pauseSpeed : o.pauseSpeed});
                        })
                    });
            });
        }
    });
})(jQuery);

 $(document).ready(function() {
        $('.rotate').rotaterator({fadeSpeed:500, pauseSpeed:6000});
 });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2 class="light content-medium center text-center soft-top--quad">
Join us for a
    <div class="rotate"> 
        <span>relaxed</span>
        <span>wonderful</span>
      	<span>crazy</span>
    </div> 
  weekend of
  <div class="rotate"> 
        <span>fun</span>
        <span>games</span>
        <span>laughter</span>
      	<span>dancing</span>
    	<span>love</span>
  </div> resulting in your
  <div class="rotate"> 
        <span>best</span>
        <span>worst</span>
        <span>most disgusting</span>
    </div>
     <div class="rotate"> 
        <span>memories</span>
        <span>hangover</span>
    </div>
		    </h2>

I was hoping to re-implement this in vanilla JavaScript instead and wondered if this would be an easy job? For example, is there anything in here which relies heavily on jQuery?

I would also appreciate any tips on how to best start with this, as I am rather new to JavaScript.

1 answer

  • answered 2018-01-19 17:04 Frébo

    In fact you need a whole rewrite. I took a look and wrote a vanilla script with the same functionality from scratch without caring about the previous logic. I used CSS transitions instead of any JavaScript animation library.

    Since this is a quick shot and nobody pays me, it's not the best and most efficient code and it could be improved using more functions and callbacks.

    There are several setTimeout and multiple DOM updates.
    We also have to care about the fact, that the other items should be hidden (using display: none so they don't use space) but we cannot fade in an already hidden item by setting display: inline; opacity: 1 at the same moment, otherwise we wouldn't see the transition.

    It could have been done so much better in ES2015+ which fewer lines, arrow functions and Promise, but since I don't know where you need this script and which browser you want to support, I wrote it in ugly old plain ES5.

    /**
     * The new fancy VanillaJS rotaterator
     * @param {string} selector 
     * @param {object} options 
     */
    function rotaterator(selector, options) {
        var defaults = {
            fadeSpeed: 500,
            pauseSpeed: 500,
            child: null
        };
    
        var options = Object.assign(defaults, options);
        var items = document.querySelectorAll(selector);
        var allSpans = [];
    
        /**
         * Fade all elements of the given array in by setting display and opacity
         * @param {array} arrElements 
         */
        function fadeElementsIn(arrElements) {
            arrElements.forEach(function (e) {
                if (e.style.display === 'none') {
                    // if we are setting from none directly to inline, we need a small delay
                    e.style.display = 'inline';
                    window.setTimeout(function () {
                        e.style.opacity = 1;
                    }, 10);
                } else
                    e.style.opacity = 1;
            });
        }
    
        /**
         * Hide all previously cached span elements by setting display to none
         */
        function hideAll() {
            allSpans.forEach(function (e) {
                e.style.display = 'none';
            });
        }
    
        /**
         * Set initial styles and transition and fade first elements in
         */
        function initialize(onInitialized) {
            var initialFadeIn = [];
            items.forEach(function (item) {
                var spans = item.querySelectorAll('span');
                spans.forEach(function (span) {
                    allSpans.push(span);
                    span.style.opacity = 0;
                    span.style.transition = (options.fadeSpeed / 1000) + 's linear';
                });
    
                initialFadeIn.push(spans[0]);
            });
    
            // finally fade the first set of elements in and call the callback
            window.setTimeout(function () {
                fadeElementsIn(initialFadeIn);
                onInitialized();
            }, 10);
        }
    
        /**
         * Fade the current items out and fade the next items in
         */
        function next() {
            window.setTimeout(function () {
                var toFadeIn = [];
    
                items.forEach(function (item) {
                    var nextIndex;
                    for (var i = 0; i < item.children.length; i++) {
                        if (item.children[i].style.opacity == 1) {
                            // fade current item out
                            item.children[i].style.opacity = 0;
    
                            // set next index to fadeIn
                            nextIndex = (i + 1 > item.children.length - 1 ? 0 : i + 1);
                        }
                    }
    
                    // save the next element to array
                    toFadeIn.push(item.children[nextIndex]);
                });
    
                // wait for fade out transition effect to complete and then fade all new elements in
                window.setTimeout(function () {
                    hideAll();
                    fadeElementsIn(toFadeIn);
    
                    // after fadeIn transition effect call this method recursive.
                    window.setTimeout(function () {
                        next();
                    }, options.fadeSpeed);
                }, options.fadeSpeed);
            }, options.pauseSpeed);
        }
    
        initialize(next);
    }
    
    ready(function () {
        rotaterator('.rotate', { fadeSpeed: 500, pauseSpeed: 6000 });
    });
    
    /**
     * Polyfill for Object.assign
     */
    if (typeof Object.assign != 'function') {
        Object.assign = function (target) {
            'use strict';
            if (target == null) {
                throw new TypeError('Cannot convert undefined or null to object');
            }
    
            target = Object(target);
            for (var index = 1; index < arguments.length; index++) {
                var source = arguments[index];
                if (source != null) {
                    for (var key in source) {
                        if (Object.prototype.hasOwnProperty.call(source, key)) {
                            target[key] = source[key];
                        }
                    }
                }
            }
            return target;
        };
    }
    
    /**
     * document.ready function without jQuery
     * @param {function} fn 
     */
    function ready(fn) {
        if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading") {
            fn();
        } else {
            document.addEventListener('DOMContentLoaded', fn);
        }
    }