// The MIT License (MIT) // Typed.js | Copyright (c) 2016 Matt Boldt | www.mattboldt.com // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. ! function(window, document, $) { "use strict"; var Typed = function(el, options) { var self = this; // chosen element to manipulate text this.el = el; // options this.options = {}; Object.keys(defaults).forEach(function(key) { self.options[key] = defaults[key]; }); Object.keys(options).forEach(function(key) { self.options[key] = options[key]; }); // attribute to type into this.isInput = this.el.tagName.toLowerCase() === 'input'; this.attr = this.options.attr; // show cursor this.showCursor = this.isInput ? false : this.options.showCursor; // text content of element this.elContent = this.attr ? this.el.getAttribute(this.attr) : this.el.textContent; // html or plain text this.contentType = this.options.contentType; // typing speed this.typeSpeed = this.options.typeSpeed; // add a delay before typing starts this.startDelay = this.options.startDelay; // backspacing speed this.backSpeed = this.options.backSpeed; // amount of time to wait before backspacing this.backDelay = this.options.backDelay; // Fade out instead of backspace this.fadeOut = this.options.fadeOut; this.fadeOutClass = this.options.fadeOutClass; this.fadeOutDelay = this.options.fadeOutDelay; // div containing strings if($ && this.options.stringsElement instanceof $) { this.stringsElement = this.options.stringsElement[0] } else { this.stringsElement = this.options.stringsElement; } // input strings of text this.strings = this.options.strings; // character number position of current string this.strPos = 0; // current array position this.arrayPos = 0; // number to stop backspacing on. // default 0, can change depending on how many chars // you want to remove at the time this.stopNum = 0; // Looping logic this.loop = this.options.loop; this.loopCount = this.options.loopCount; this.curLoop = 0; // for stopping this.stop = false; // custom cursor this.cursorChar = this.options.cursorChar; // shuffle the strings this.shuffle = this.options.shuffle; // the order of strings this.sequence = []; // All systems go! this.build(); }; Typed.prototype = { constructor: Typed, init: function() { // begin the loop w/ first current string (global self.strings) // current string will be passed as an argument each time after this var self = this; self.timeout = setTimeout(function() { for (var i=0;i curString.length) { break; } } curStrPos++; tag += endTag; } } // timeout for any pause after a character self.timeout = setTimeout(function() { if (curStrPos === curString.length) { // fires callback function self.options.onStringTyped(self.arrayPos); // is this the final string if (self.arrayPos === self.strings.length - 1) { // animation that occurs on the last typed string self.options.callback(); self.curLoop++; // quit if we wont loop back if (self.loop === false || self.curLoop === self.loopCount) return; } self.timeout = setTimeout(function() { self.backspace(curString, curStrPos); }, self.backDelay); } else { /* call before functions if applicable */ if (curStrPos === 0) { self.options.preStringTyped(self.arrayPos); } // start typing each new char into existing string // curString: arg, self.el.html: original text inside element var nextString = curString.substr(0, curStrPos + 1); if (self.attr) { self.el.setAttribute(self.attr, nextString); } else { if (self.isInput) { self.el.value = nextString; } else if (self.contentType === 'html') { self.el.innerHTML = nextString; } else { self.el.textContent = nextString; } } // add characters one by one curStrPos++; // loop the function self.typewrite(curString, curStrPos); } // end of character pause }, charPause); // humanized value for typing }, humanize); }, backspace: function(curString, curStrPos) { var self = this; // exit when stopped if (this.stop === true) { return; } if (this.fadeOut){ this.initFadeOut(); return; } // varying values for setTimeout during typing // can't be global since number changes each time loop is executed var humanize = Math.round(Math.random() * (100 - 30)) + this.backSpeed; self.timeout = setTimeout(function() { // ----- this part is optional ----- // // check string array position // on the first string, only delete one word // the stopNum actually represents the amount of chars to // keep in the current string. In my case it's 14. // if (self.arrayPos == 1){ // self.stopNum = 14; // } //every other time, delete the whole typed string // else{ // self.stopNum = 0; // } if (self.contentType === 'html') { // skip over html tags while backspacing if (curString.substr(curStrPos).charAt(0) === '>') { var tag = ''; while (curString.substr(curStrPos - 1).charAt(0) !== '<') { tag -= curString.substr(curStrPos).charAt(0); curStrPos--; if (curStrPos < 0) { break; } } curStrPos--; tag += '<'; } } // ----- continue important stuff ----- // // replace text with base text + typed characters var nextString = curString.substr(0, curStrPos); self.replaceText(nextString); // if the number (id of character in current string) is // less than the stop number, keep going if (curStrPos > self.stopNum) { // subtract characters one by one curStrPos--; // loop the function self.backspace(curString, curStrPos); } // if the stop number has been reached, increase // array position to next string else if (curStrPos <= self.stopNum) { self.arrayPos++; if (self.arrayPos === self.strings.length) { self.arrayPos = 0; // Shuffle sequence again if(self.shuffle) self.sequence = self.shuffleArray(self.sequence); self.init(); } else self.typewrite(self.strings[self.sequence[self.arrayPos]], curStrPos); } // humanized value for typing }, humanize); }, // Adds a CSS class to fade out current string initFadeOut: function(){ self = this; this.el.className += ' ' + this.fadeOutClass; this.cursor.className += ' ' + this.fadeOutClass; return setTimeout(function() { self.arrayPos++; self.replaceText('') self.typewrite(self.strings[self.sequence[self.arrayPos]], 0); }, self.fadeOutDelay); }, // Replaces current text in the HTML element replaceText: function(str) { if (this.attr) { this.el.setAttribute(this.attr, str); } else { if (this.isInput) { this.el.value = str; } else if (this.contentType === 'html') { this.el.innerHTML = str; } else { this.el.textContent = str; } } }, // Shuffles the numbers in the given array. shuffleArray: function(array) { var tmp, current, top = array.length; if(top) while(--top) { current = Math.floor(Math.random() * (top + 1)); tmp = array[current]; array[current] = array[top]; array[top] = tmp; } return array; }, // Start & Stop currently not working // , stop: function() { // var self = this; // self.stop = true; // clearInterval(self.timeout); // } // , start: function() { // var self = this; // if(self.stop === false) // return; // this.stop = false; // this.init(); // } // Reset and rebuild the element reset: function() { var self = this; clearInterval(self.timeout); var id = this.el.getAttribute('id'); this.el.textContent = ''; if (typeof this.cursor !== 'undefined' && typeof this.cursor.parentNode !== 'undefined') { this.cursor.parentNode.removeChild(this.cursor); } this.strPos = 0; this.arrayPos = 0; this.curLoop = 0; // Send the callback this.options.resetCallback(); } }; Typed.new = function(selector, option) { var elements = Array.prototype.slice.apply(document.querySelectorAll(selector)); elements.forEach(function(element) { var instance = element._typed, options = typeof option == 'object' && option; if (instance) { instance.reset(); } element._typed = instance = new Typed(element, options); if (typeof option == 'string') instance[option](); }); }; if ($) { $.fn.typed = function(option) { return this.each(function() { var $this = $(this), data = $this.data('typed'), options = typeof option == 'object' && option; if (data) { data.reset(); } $this.data('typed', (data = new Typed(this, options))); if (typeof option == 'string') data[option](); }); }; } window.Typed = Typed; var defaults = { strings: ["These are the default values...", "You know what you should do?", "Use your own!", "Have a great day!"], stringsElement: null, // typing speed typeSpeed: 0, // time before typing starts startDelay: 0, // backspacing speed backSpeed: 0, // shuffle the strings shuffle: false, // time before backspacing backDelay: 500, // Fade out instead of backspace fadeOut: false, fadeOutClass: 'typed-fade-out', fadeOutDelay: 500, // milliseconds // loop loop: false, // false = infinite loopCount: false, // show cursor showCursor: true, // character for cursor cursorChar: "|", // attribute to type (null == text) attr: null, // either html or text contentType: 'html', // call when done callback function callback: function() {}, // starting callback function before each string preStringTyped: function() {}, //callback for every typed string onStringTyped: function() {}, // callback for reset resetCallback: function() {} }; }(window, document, window.jQuery);