JSFiddle - React, Tailwind, and code Playground

by Henning Bopp

HTML

<textarea id="input" style="width: 100%; min-height: 35vh;">Offenbach (energate) ­- Der Mannheimer Regionalversorger MVV setzt beim digitalen Mess- und Zählwesen auf den hauseigenen IT-Servicedienstleister Soluvia. Dieser nimmt nach eigenen Angaben als einer der bundesweit ersten Smart-Meter-Gateway-Administratoren die Arbeit auf. Konkret fokussiere Soluvia ihre Arbeit zunächst auf Netze der MVV in Kiel, Mannheim und Offenbach mit 560.000 Zählpunkten, teilte die in Offenbach ansässige MVV-Tochter mit. 

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptas commodi numquam doloribus repudiandae? Totam voluptatibus esse, nisi beatae laboriosam, reprehenderit rerum tempora consectetur ipsa, magnam dignissimos numquam dolore voluptates aperiam.

äöüßÄÖÜ?
Œ &lt;strong&gt; 	 narf &amp;amp;

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

CSS

._m {
  display: inline-block;
}

._spc {
  color: #359cda;
}

._brk {
  color: red;
}

._uc {
  background-color: red;
  color: white;
}

._np {
  width: 2px;
  margin-right: -2px;
  min-height: 10px;
  background-color: maroon;
}

._tab {
  min-width: 15px;
  text-align: center;
  background-color: #f3f3f3;
}

JavaScript

(function(window, document){
    const i = document.getElementById('input');
    const o = document.getElementById('output');
    let capture = false;

    function adopt(init){
        if(!capture && !init) return;

        const is = window.getComputedStyle(i);
        let toAdopt = ['width', 'height'];
        init && toAdopt.push('font-style', 'font-variant', 'font-weight', 'font-size', 'font-family');
        init && toAdopt.push('lineHeight');
        init && toAdopt.push('padding-bottom', 'padding-left', 'padding-right', 'padding-top');
        init && toAdopt.push('border-top-width', 'border-top-style', 'border-top-color');
        init && toAdopt.push('border-right-width', 'border-right-style', 'border-right-color');
        init && toAdopt.push('border-bottom-width', 'border-bottom-style', 'border-bottom-color');
        init && toAdopt.push('border-left-width', 'border-left-style', 'border-left-color');
        toAdopt.forEach(function(e){ o.style[e] = is[e]; });
    }

    document.body.addEventListener('mousedown', function(){ capture = true; });
    document.body.addEventListener('mouseup',   function(){ capture = false; });
    document.body.addEventListener('mousemove', function(){ adopt(); });

    let debounce;
    function change(){
        debounce && window.clearTimeout(debounce);
        debounce = window.setTimeout(doChange, 100);
    }
    function doChange(){
        let html = i.value;

        // Escape possible HTML
        html = html.replace(/&/g, '&amp;');
        html = html.replace(/</g, '&lt;');
        html = html.replace(/>/g, '&gt;');

        // _spc ==> Single Space Char chr(32)
        html = html.replace(/ /g, '<span class="_m _spc">&middot;</span>');
        
        // _tab ==> Tab Stops chr(9)
        html = html.replace(/\t/g, '<span class="_m _tab">🠖</span>');
        
        // CarriageReturn chr(13)
        html = html.replace(/\r/g, '');
        
        // _brk ==> NewLine chr(10)
        html =...