An Auto-Updating Data URI Text Editor

tl;dr: Click this, type some stuff, watch the URL, and then come back if you’re curious as to what’s going on.

Ok, quick recap:

I was pretty enamored by that and used a variant of it at work for awhile to jot down my notes during the day. Like all tinkerers, I couldn't leave well enough alone, and so here's my fancier but definitely-not-the-smallest-and-so-not-as-novel version:

Clickable:
data:text/html,<body id='b'><textarea spellcheck=%22false%22 id=%22t%22></textarea><script>var f, t=document.getElementById(%22t%22), b=document.body, v=t.value, c=parseInt(b.getAttribute(%22c%22)) || t.value.length;t.focus();document.title=t.value.split(%22\n%22)[0] || %22...%22;t.className=%22s%22;function updateUrl(){clearInterval(f);f=setTimeout(function(){if (v===t.value){return false;}t.className=%22%22;t.innerHTML=encodeURIComponent(t.value);var s=%22data:text/html,<body id='b'%22;if (!!t.selectionStart){s +=%22 c='%22 + t.selectionStart + %22'%22;}s +=%22>%22 + b.innerHTML;window.location.href=s;}, 500);}t.addEventListener(%22keyup%22, updateUrl);if (!!t.setSelectionRange){t.setSelectionRange(c,c);setTimeout(function(){t.setSelectionRange(c,c);}, 10);}</script><style>textarea{position: fixed;top: 0;left: 0;width: 100%;height: 100%;overflow: auto;margin: 0;padding: 10px;background: black;font: 14px/20px monospace;outline: none;border: none;box-sizing: border-box;cursor: wait;color: gray;}textarea.s{cursor: text;color: white;}</style><!--http://iamnotagoodartist.com/web/an-auto-updating-data-uri-text-editor -->

Breakdown

Mine uses a textarea instead of a contenteditable <pre>, since it's made to be fullscreen and thus doesn't need contenteditable's auto-adjustment to variably dimensioned content. (Actually, the one regret is that my text editor now won't use any of the HTML5 spec and so is not as trendy... Sniffle, sniffle.)

So we've got a fixed and fullscreen input. We'll use its first line (if it has one) as the document title and bind a debounced* function to its keyup event. When the function executes successfully, we write a new URL using the existing body's HTML, the input's encoded value, and its caret/selection position, and then we'll redirect the user to it, setting the caret position and repeating the process indefinitely.

* Debouncing is a method of rate limiting. It takes a time duration and waits that long after each firing to run, resetting and delaying execution if fired before the duration is up. Its more common sibling is throttling, which will run at most once per time duration while firing and will not reset.

Unordered, Non-Narrative Findings

  • If the software and its data share the same limited storage mechanism, brevity must trump legacy support: no CSS3 vendor prefixes, not a lot of feature sniffing or graceful degradation, etc. All JS is vanilla and doesn't use external libraries.
  • Like local files, Data URIs are prohibited from a number of things for security reasons; cookies, localStorage/sessionStorage, and any history manipulation are out the window. Thus, there's no saving beyond the URL and updates happen with old-fashioned redirects instead of the cooler pushState.
  • I'm storing caret position as just a c attribute to save some bytes. There may be a better way to do it, and I'm not sure what the technical implications are for arbitrary attributes like that instead of, say, a data attribute.
  • Dirty/clean save status is handled with a CSS class to gray out the text and use the wait cursor. May be overkill.
  • textareas have both value and innerHTML. At init they are the same, but only value changes as the user interacts with it; no HTML is changed. Because we're using the body's innerHTML to rewrite the URL, we must set the input's innerHTML to its value, encodeURIComponented for safety.
  • Nobody will call the cops if you don't close your body tag.

Still Could Use...

  • Different browsers have different URL limits. Ideally, this would have some sort of reservoir meter.
  • Code and markup is properly escaped, but trying to save anything with </textarea> will effectively close the input and bork all the text thereafter. It escapes me how to get around that at the moment...
  • Safari takes a couple microseconds to focus an element and won't allow caret setting while it's unfocused, so there's some awkward and redundant setTimeout stuff in there to give it some time. I'd like for that to be less crummy.

Unavoidable Gotchas

  • Data URIs aren't supported in IE. Tested successfully in Chrome, Firefox, Safari, and Opera.
  • Undo and redo can't persist over redirect. (But, interestingly, you can use native back and forward to kind of view the text's history.)
  • Browsers can interpret Data URIs, yes, but most software won't automatically turn them into links like traditional HTTP URLs. So copy-and-pasting into chats will always have to be prefaced with "Hey, copypaste this into a new tab, k?"

How is this useful?

Honestly? Probably only as an academic pursuit.

But try this: open it up and write something, copy the URL and paste it into another tab or browser. Your text persisted without a database, any external script, or proprietary Cloud gizmo. No legal order can cease it to exist, and you didn't even need an internet connection. I think that's pretty neat!