typewriter.js 117 regels JavaScript
/**
 * @param {string|HTMLElement} elementOrId - element or ID of element containing text to type
 * @param {object} [opts] - options
 * @param {number} [opts.speed=50] - milliseconds per character
 * @param {number} [opts.delay=0] - initial delay before typing starts (ms)
 * @param {boolean} [opts.cursor=true] - show blinking cursor
 * @param {boolean} [opts.loop=false] - restart after finishing
 * @param {number} [opts.loopDelay=2000] - pause before restarting (ms)
 * @example
 *     <p id="hero">Welcome to our website.</p>
 *     <script src="/js/typewriter.js"></script>
 *     <script>new BonarooTypewriter("hero").start();</script>
 */
function BonarooTypewriter(elementOrId, opts) {
  opts = opts || {};

  this.element = typeof elementOrId === "string"
    ? document.getElementById(elementOrId)
    : elementOrId;

  if (!this.element) {
    throw new Error("BonarooTypewriter: element not found: " + elementOrId);
  }

  this.fullText = this.element.textContent;
  this.speed = typeof opts.speed === "number" ? opts.speed : 50;
  this.delay = typeof opts.delay === "number" ? opts.delay : 0;
  this.showCursor = opts.cursor !== false;
  this.loop = opts.loop === true;
  this.loopDelay = typeof opts.loopDelay === "number" ? opts.loopDelay : 2000;

  this._charIndex = 0;
  this._timeoutId = null;
  this._cursorElement = null;

  if (this.showCursor) {
    this._injectCursorStyle();
  }
}

BonarooTypewriter.CURSOR_CLASS = "bonaroo-tw-cursor";
BonarooTypewriter.STYLE_ID = "bonaroo-tw-style";

BonarooTypewriter.prototype._injectCursorStyle = function () {
  if (document.getElementById(BonarooTypewriter.STYLE_ID)) return;

  var style = document.createElement("style");
  style.id = BonarooTypewriter.STYLE_ID;
  style.textContent =
    "." + BonarooTypewriter.CURSOR_CLASS + " {" +
    "  display: inline-block;" +
    "  width: 2px;" +
    "  height: 1em;" +
    "  background: currentColor;" +
    "  margin-left: 2px;" +
    "  vertical-align: text-bottom;" +
    "  animation: bonaroo-tw-blink 0.7s step-end infinite;" +
    "}" +
    "@keyframes bonaroo-tw-blink {" +
    "  50% { opacity: 0; }" +
    "}";

  document.head.appendChild(style);
};

BonarooTypewriter.prototype._createCursor = function () {
  if (!this.showCursor) return;

  this._cursorElement = document.createElement("span");
  this._cursorElement.className = BonarooTypewriter.CURSOR_CLASS;
  this._cursorElement.setAttribute("aria-hidden", "true");
  this.element.appendChild(this._cursorElement);
};

BonarooTypewriter.prototype._removeCursor = function () {
  if (this._cursorElement && this._cursorElement.parentNode) {
    this._cursorElement.parentNode.removeChild(this._cursorElement);
    this._cursorElement = null;
  }
};

BonarooTypewriter.prototype.start = function () {
  this._charIndex = 0;
  this.element.textContent = "";
  this._createCursor();

  if (this.delay > 0) {
    this._timeoutId = setTimeout(this._type.bind(this), this.delay);
  } else {
    this._type();
  }

  return this;
};

BonarooTypewriter.prototype._type = function () {
  if (this._charIndex < this.fullText.length) {
    this._removeCursor();
    this.element.textContent = this.fullText.substring(0, this._charIndex + 1);
    this._createCursor();
    this._charIndex++;
    this._timeoutId = setTimeout(this._type.bind(this), this.speed);
  } else {
    if (this.loop) {
      this._timeoutId = setTimeout(this.start.bind(this), this.loopDelay);
    }
  }
};

BonarooTypewriter.prototype.reset = function () {
  clearTimeout(this._timeoutId);
  this._timeoutId = null;
  this._charIndex = 0;
  this._removeCursor();
  this.element.textContent = this.fullText;

  return this;
};

/**
 * Write the element before the current executing script element.
 * Handy for inline usage, similar to BonarooTimer.
 */
BonarooTypewriter.prototype.write = function () {
  var el = document.createElement("span");
  el.textContent = this.fullText;
  document.currentScript.insertAdjacentElement("beforebegin", el);

  this.element = el;
  this.start();

  return this;
};