HTML Typer

by soulwire

HTML

<div id="output"></div>

Babel + JSX

const html = 'this <a href="#">is</a> some<br/>text <h1>big</h1> <strong>cool <em>beans</em></strong>&hellip; :)';
// t,h, ,<a href="">,i
// split into columns
// cells can be empty but keep tags in place
// they can then either collapse or example
// only perform scramble / type on non-tag cells

// for swapping text:
// take 2 arrays for old and new
// pad shortest to match longest
// third array of bools whether to use new
// progressively toggle to new
// merge

const shuffle = (a) => {
  let n, i = a.length - 1;
  for (; i > 0; i--) {
    n = Math.floor(Math.random() * (i + 1));
    [ a[n], a[i] ] = [ a[i], a[n] ];
  }
  return a;
};

const reTag = /<[^>]+>/;
const reEntity = /&[^;\s]+;/;
const reMatchStart = new RegExp(`^(${reTag.source}|${reEntity.source})`);

// reduce string from front taking tags that match at start
let str = html;
const chars = [];
while (str.length) {
	//let match = str.match(/^<[^>]+>/);
  //let match = str.match(/^(<[^>]+>|&[^;\s]+;)/);
  let match = str.match(reMatchStart);
  let item = match ? match[0] : str[0];
  str = str.substr(item.length);
  chars.push(item);
}
const isTag = chars.map(c => c.length > 1);
//const state = chars.map(c => c.length > 1 ? c : ' ');
const state = chars.map(char => {
	// maintain tags
	if (reTag.test(char)) { return char; }
  // maintain spaces
  if (char === ' ') { return '&nbsp;' }
  return ' ';
});
const queue = chars.reduce((memo, char, index) => {
	// not tag or space (allow spaces for different effect)
  //console.log(char, reEntity.test(char))
	if (reEntity.test(char) || (char.length === 1 && char !== ' ')) {
  	memo.push({
    	char,
      index
    });
  }
  return memo;
}, []);
console.log(chars);
console.log(isTag);
console.log(state);
console.log(JSON.stringify(queue));

shuffle(queue);

const el = document.getElementById('output');
let interval = setInterval(() => {
	const item = queue.pop();
  state[item.index] = item.char;
  el.innerHTML = state.join('');
  if (!queue.length) {
 ...