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); } }
page revision: 161, last edited: 18 Jan 2021 07:24






