Dummy Txtarea

f(null,a=>(a.push({val: '[[collapsible]]', type: 1}), a))


<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="./2"/>
</head>
<body>
<textarea id="error" rows="7" style="margin:0 0 2em;width:100%" placeholder="error"></textarea>
<textarea id="others" rows="7" style="margin:0 0 2em;width:100%" placeholder="others"></textarea>
 
<hr/>
<p>dummy textarea:</p>
 
<script>
window.onerror=e=>(document.querySelector('#error').value+=`${e}\n`);
var _log = s =>(document.querySelector('#others').value+=`${s}\n`);
var debug = {};
 
var e = (t,p=document.body)=>(x=>(p.appendChild(x),x))(document.createElement(t));
var w = e('div');
w.classList.add('wrap');
 
(()=>(async(d,br,f,isTouch,cand)=>(
d.classList.add('line'),
d._val=[],
d._sel_range=[],
cand=0,
d._txt=e('textarea',w),
d._txt.classList.add('da'),
 
  isTouch = await (new Promise(r=>((t,d,x)=>(
    (d=e=>(t=!!e.changedTouches,document.removeEventListener('touchstart',d),document.removeEventListener('mousemove',d))),
    document.addEventListener('touchstart',d),
    document.addEventListener('mousemove',d),
    (x = () => t==-1?setTimeout(x,100):r(t))()
  ))(-1))),
 
(f=(_,dec=s=>s)=>(_ && isTouch && (_.type=='select' || _.type=='selectstart' || _.type=='touchstart') && _.target==d._sel)
  ? (_.target.setSelectionRange(0,1))
  : (
 
d._sel=d._sel||[],
cand && (
  _ && _.key == 'ArrowRight' && (
    cand = 0,
    d._txt.value = d._val.map(v=>v.classList.contains('br')?'\n':v.innerText).filter(v=>v!=='\u200b').join('').trim(),
    console.log('x: ', d._txt.value)
  )
),
 
(
  d._val=[],d._sel=[],d.innerHTML='',
  br={n:0,_:[]},
  [...dec(d._txt.value.split('').map(x=>{
    return {val: x, type: 0}
  })), {val: '\u200b', type: 0}].forEach(v=>(s=>(
    d._val.push(s),
    s.innerHTML=v.val,
    s._parent = d,
    v.type && (cand=1, s.classList.add('cand')),
    br.n++,
    (
      v.val=='\n'
        ? (
          s.classList.add('br'),
          br._.push(s),
          s._br = 0+br.n,
          br.n = 0
        )
        : v.val.match(/^[^\x01-\x7E\xA1-\xDF]$/) && (
          s.classList.add('fw')
        )
      )
    )
  )(e('span',d))),
  (m=>br._.forEach(b=>(b.style.gridColumnStart = b._br>=m ? b._br%m : b._br)))(
    Math.floor( d.getBoundingClientRect().width / Number(window.getComputedStyle(d)['gridTemplateColumns'].match(/^([\d\.]+?)px/)[1]))
  ),
 
  _&&((d._car=d._val[d._txt.selectionStart]).classList.add('caret'),(b=>(~['keyup','keydown','input','paste','cut'].indexOf(_.type) && ((e,p)=>p.top <e.bottom&&p.bottom>e.top)(b,w.getBoundingClientRect()) || (w.scrollTop = b.top - d.getBoundingClientRect().top), d._txt.style.top=b.top+'px',d._txt.style.left=b.left+'px',
 
console.log(b)
 
))(d._car.getBoundingClientRect()))
),
 
d._txt.selectionStart==d._txt.selectionEnd?d._sel.forEach(s=>s.classList.remove('selected')):(d._sel=d._val.slice(d._txt.selectionStart,d._txt.selectionEnd).forEach(s=>s.classList.add('selected'))))
)(),
 
  w.onclick=_=>(isTouch && _.target==d._sel) || (
  _.target!==d._pst && (
    document.getSelection().isCollapsed
    ?(x=>(
      d._car.classList.remove('caret'),
      d._car=(_.target==w||_.target==d)?d._val[d._val.length-1]:_.target,
      (e=>{
        while(!e.classList.contains('cand')) {
          if(e.previousElementSibling) {
            e = e.previousElementSibling
          }else {
            break
          }
        }
        return e
      })(d._car),
      x = x == d._car,
      d._car.classList.add('caret'),
      x && (
        d._car.classList.add('menu')/*,
        (ul=>(
 
          (async(li,state,data)=>(
            {state} = await navigator.permissions.query({name:'clipboard-read'}),
            data = (state == 'granted' || state == 'prompt') && (
              (await navigator.clipboard.readText())
            ),
            data ? li.remove() : (li.innerHTML = `Paste (Ctrl+V):\n${data}`, li.onclick = _ => ((n,bef,aft)=>(
              bef = d._txt.value.slice(0,n) + data,
              aft = d._txt.value.slice(n),
              d._txt.value = bef + aft,
              d._txt.setSelectionRange(bef.length,bef.length),
              d._txt.focus(),
              f()
            ))(d._val.indexOf(d._car)))
          ))(e('li', ul))
 
        ))(e('ul', d._car))*/
      ),
      d._txt.focus(),
      (n=>d._txt.setSelectionRange(n,n))(d._val.indexOf(d._car))
    ))(d._car)
    :(d._txt.focus(), console.log(d._sel_range), d._sel_range.length&&((r=>d._txt.setSelectionRange(r[0],r[1]))(d._sel_range), d._sel_range=[]))
 
  )
  ),
 
  debug.f = f,
 
  d.contentEditable = true,
  d.onkeydown = e => e.preventDefault(),
  d.onpaste = e => (
    e.target.innerHTML = e.clipboardData.getData('text') + e.target.innerHTML,
    d._txt.value = d._val.map(v=>(
      v.classList.contains('br')?v.innerText+'\n':v.innerText
    ).replace(/\u200b/g,'')).filter(v=>v&&v!=='\u200b').join('').trim(),
    e.preventDefault(),
    f()
  ),
  d.oncut = e => (
    d._txt.value = d._val.map(v=>(
      v.classList.contains('br')?v.innerText+'\n':v.innerText
    ).replace(/\u200b/g,'')).filter(v=>v&&v!=='\u200b').join('').trim(),
    e.preventDefault(),
    f()
  ),
 
  _log(`${isTouch?'touch':'mouse'} device detected`),
 
  isTouch || (w.style.cursor = 'text'),
 
  document.addEventListener('selectionchange',e=>((s,r,a,b)=>{
    try {
        (r=s.getRangeAt(0), [a,b]=[r.startContainer,r.endContainer].map(v=>d._val.indexOf(
            (x=>x._parent==d?x:null)(v.nodeName=='#text'?v.parentElement:v)
        )).sort((a,b)=>a-b));
        a==b||~a&&(d._sel_range=[a,b+1],_log(d._sel_range));
    }catch(e){}
  })(document.getSelection())),
 
['keyup','keydown','keypress','mousedown','touchstart','input','paste','cut','select','selectstart'].forEach(x=>d._txt.addEventListener(x,e=>(f(e)))),
 
(obs=>obs.observe(w, {
  attriblutes: true,
  attributeFilter: ["style"]
}))(new MutationObserver(()=>f())),
 
d._txt.onblur=_=>d._car.classList.remove('caret'),
d._car=d._val[d._val.length-1],
 
d))(e('div',w),0))();
</script>
</body>
.wrap {
  background: #fff;
  border: 1px solid #ddd;
  cursor: pointer;
  max-width: 500px;
  overflow: auto;
  padding: .5em;
  position: relative;
  resize: both;
  transition: border-color .25s ease;
}
 
.wrap:focus-within {
  border-color: rgba(70,136,224,1);
}
.wrap textarea {
  height: 1em;
  overflow: hidden;
  position: fixed;
  resize: none;
  width: 1em;
}
.wrap textarea.da {
  opacity: 0;
  pointer-events: none;
}
.wrap .line {
  caret-color: transparent;
  display: grid;
  font-family: 'Courier New', Courier, monospace;
  font-size: .8rem;
    grid-template-columns: repeat(auto-fit, .7em);
    grid-template-rows: 1.1em;
  line-height: 1;
  margin: 0;
  outline: none;
  padding: 0;
  position: relative;
}
.wrap .line:focus {
  outline: none;
}
.wrap .line span {
  align-items: flex-end;
  border-left: 1px solid rgba(0,0,0,0);
  box-sizing: border-box;
  display: inline-flex;
}
.wrap .line span.fw {
  font-size: .64em;
}
.wrap .line span.br {
  font-size: .64em;
  grid-column-end: -1;
}
.wrap .line span.br::before {
  content: '\21B5';
}
.wrap .line span.caret {
  animation: carBlink 1s infinite;
  border-left: 1px solid rgba(0,0,0,1);
}
.wrap .line span.cand {
  color: #ddd;
  pointer-events: none;
}
.wrap .line span.selected {
  animation: none;
  background: rgba(70,136,224,1);
  color: #fff;
  border-color: rgba(70,136,224,1);
}
.wrap .line span.br.selected {
    background: transparent;
    border: none;
}
.wrap .line span.br.selected::before {
    background: rgba(70,136,224,1);
}
.wrap .line span:last-child {
  padding: 0 .5em;
}
.wrap .line span:first-child:last-child {
  grid-column: 1 / -1;
}
 
.wrap .menu ul {
  background: #888;
  border: 1px solid #888;
  font-size: .64rem;
  margin: 0;
  list-style: none;
  padding: .5em;
  position: absolute;
}
@keyframes carBlink {
  0%,50% {
    border-color: rgba(0,0,0,1);
  }
  75%,100% {
    border-color: rgba(0,0,0,0);
  }
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License