JSFiddle - React, Tailwind, and code Playground

by ocanal

HTML

<div class="container" id="container">
  <div class="backdrop" id="backdrop">
    <div class="highlights" id="highlights"></div>
  </div>
  <textarea id="textarea">12 test 22 2</textarea>
</div>

CSS

@import url(https://fonts.googleapis.com/css?family=Open+Sans);

*, *::before, *::after {
  box-sizing: border-box;
}

body {
  margin: 30px;
  background-color: #f0f0f0;
}

.container, .backdrop, textarea {
  width: 460px;
  height: 180px;
}

.highlights, textarea {
  padding: 10px;
  font: 20px/28px 'Open Sans', sans-serif;
  letter-spacing: 1px;
}

.container {
  display: block;
  margin: 0 auto;
  transform: translateZ(0);
  -webkit-text-size-adjust: none;
}

.backdrop {
  position: absolute;
  z-index: 1;
  border: 2px solid #685972;
  background-color: #fff;
  overflow: auto;
  pointer-events: none;
  transition: transform 1s;
}

.highlights {
	white-space: pre-wrap;
	word-wrap: break-word;
	color: #000;
}

textarea {
  display: block;
  position: absolute;
  z-index: 2;
  margin: 0;
  border: 2px solid #74637f;
  border-radius: 0;
  color: transparent;
  caret-color: #000;
  background-color: transparent;
  overflow: auto;
  resize: none;
  transition: transform 1s;
}

.number {
  border-radius: 3px;
  color: red;
  background-color:transparent;
}

button {
  display: block;
  width: 300px;
  margin: 30px auto 0;
  padding: 10px;
  border: none;
  border-radius: 6px;
  color: #fff;
  background-color: #74637f;
  font: 18px 'Opens Sans', sans-serif;
  letter-spacing: 1px;
  appearance: none;
  cursor: pointer;
}

.perspective .backdrop {
  transform:
    perspective(1500px)
    translateX(-125px)
    rotateY(45deg)
    scale(.9);
}

textarea:focus, button:focus {
  outline: none;
  box-shadow: 0 0 0 2px #c6aada;
}

JavaScript

var $container = document.getElementById('container');
var $backdrop = document.getElementById('backdrop');
var $highlights = document.getElementById('highlights');
var $textarea = document.getElementById('textarea');
var $toggle = document.getElementById('button');

// yeah, browser sniffing sucks, but there are browser-specific quirks to handle that are not a matter of feature detection
var ua = window.navigator.userAgent.toLowerCase();
var isIE = !!ua.match(/msie|trident\/7|edge/);
var isWinPhone = ua.indexOf('windows phone') !== -1;
var isIOS = !isWinPhone && !!ua.match(/ipad|iphone|ipod/);

function applyHighlights(text) {
  text = text
    .replace(/\n$/g, '\n\n')
    .replace(/\d/g, '<span class="number">$&</span>');
  
  if (isIE) {
    // IE wraps whitespace differently in a div vs textarea, this fixes it
    text = text.replace(/ /g, ' <wbr>');
  }
  
  return text;
}

function handleInput() {
  var text = $textarea.value;
  var highlightedText = applyHighlights(text);
  $highlights.innerHTML = highlightedText;
}

function handleScroll() {
  var scrollTop = $textarea.scrollTop;
  $backdrop.scrollTop = scrollTop;
  
  var scrollLeft = $textarea.scrollLeft;
  $backdrop.scrollLeft = scrollLeft;
}

function fixIOS() {
  // iOS adds 3px of (unremovable) padding to the left and right of a textarea, so adjust highlights div to match
  //$highlights.css({
   // 'padding-left': '+=3px',
  //  'padding-right': '+=3px'
 // });
}

function bindEvents() {
  $textarea.oninput = handleInput;
  $textarea.onscroll = handleScroll; 
}

if (isIOS) {
  fixIOS();
}

bindEvents();
handleInput();