My impression from this is that "take the text nodes and format them with whitespace that corresponds to the browser layout" is a vague enough definition that browsers can't agree on precisely how to do that. So it seems like it shouldn't be standardised, but maybe a JS lib could step in to come up with a reliable way of doing it?
Completely agreed. There are such a ridiculous number of edge cases here, that it makes much more sense for this to be done by a JavaScript library that either comes with a huge number of configuration flags (which would be totally inappropriate for a built-in DOM function), or else whose source code is designed to be edited directly. (E.g. do you want a bullet to become a "•" or a "*" or a "-", etc.)
I'm working on text editor with real-time spelling correction where `innerText`-like retrieval has to happen quite frequently; it's what pushed me to research the state of affairs with `innerText` in the first place.
It pains me that we don't have a quick, native way of doing that; that I have to traverse the DOM manually instead — a horribly, horribly slow process — essentially writing what `innerText` was supposed to be.
I guess HTML/DOM were never really designed to be good at a task like this. The closest thing is Selection/Ranges API but it's still way behind in terms of performance.
The fact that there's `innerText` in some browser sarcastically starring in your face ("I'm here, but you can't really use me") just makes this so much crappier.
I guess a lot of this boils down to performance. It's certainly possible to emulate `innerText` but we can't possibly match native speed. And speed is crucial for any kind of real-time editing that needs to have a quick access to plain text version of an element.
"essentially writing what `innerText` was supposed to be"
For your use case. Making, as you mentioned, a huge number of edge case decisions. So if innerText was standardized to your use case what would it be good for other than your specific project?
Pulling large chunks of plain text as a string to process for any low latency tasks constrains the size of text you can reasonably work with. JS strings are immutable as well, that's a lot of copying. So you can't be grabbing the whole document at once with innerText, but you still want it to handle line breaks. So innerText is useful for a text editor where the chunks are more than one line long but still fairly short.
Another issue is this whole handling of converting lists and tables to some kind of plain text markup. Is that a two translation? Are tabs going to turn into table elements if I have them in a string and set innerText? That seems pretty crazy, but not doing so would just make it very weirdly asymmetrical.
I liked the article, lot's of good info and stuff I didn't know. But I did come away thinking "Thank you Mozilla, at least someone is pushing for sanity". How much horribly slower do you want the DOM to be?
> So if innerText was standardized to your use case what would it be good for other than your specific project?
I don't think this is a very specific task. As you've seen in the post, other people have similar requirements (text editor, selection retrieval, etc.) and similar desire for keeping `innerText`.
> Pulling large chunks of plain text as a string to process for any low latency tasks constrains the size of text you can reasonably work with. JS strings are immutable as well, that's a lot of copying.
Pulling large chunks of plain text — that's a plausible argument, but no different than pulling, say, `innerHTML` (large chunk) or `textContent` (large chunk) or really anything else in JS land (which if often full of strings — JSON, templates, whatever).
> Another issue is this whole handling of converting lists and tables to some kind of plain text markup. Is that a two translation? Are tabs going to turn into table elements if I have them in a string and set innerText? That seems pretty crazy, but not doing so would just make it very weirdly asymmetrical.
That's a very good point! But don't forget that we already have a standard way of setting element contents from plain text — `textContent`. Yes, innerText setting would have to be assymetrical; I doubt anyone would want to go down the rabbit hole of backwards conversion. Everyone usually agrees that when it comes to setting, `innerText` should simply act like `textContent` (to quote spec: "on setting, no parsing is performed either, the input string is taken as pure textual content.")
> How much horribly slower do you want the DOM to be?
Well, as app devs, we still need to implement plain text retrieval. By not having it natively in Mozilla, the joke is really on the users — more strings (in JS land), more DOM iteration, more everything. How is that for a "slow DOM"? ;)
> As you've seen in the post, other people have similar requirements (text editor, selection retrieval, etc.) and similar desire for keeping `innerText`.
> Well, as app devs, we still need to implement plain text retrieval.
This is the were we disagree, and I'm saying that as someone who's current hobby project is a browser based text editor.
Converting a 2D layout of nodes with both layout directives and text content to a string is a lossy operation. It's ambiguous. There are HTML elements that prevent that like textboxes and pre nodes. If you add layout to your text, then you have to deal with it. The DOM doesn't include a way to encode your conversion needs and it should not.
I think if you need to implement plain text retrieval then you've made a design error. At the very least you are fighting the environment in which you chose to implement your software.
Have you thought of intercepting keyboard strokes to maintain
your own internal state of the text ?
Instead of having Keyboard-->HTML-(extract)->innerText you would have Keyboard-->innerText-(render)->HTML. Contenteditable is a honey pot, it makes you think that implementing a text editor will be easy, until you hit all the corner cases.
Unfortunately not possible since the editor is part of a browser extension and is activated on any page with textarea/contenteditable. Text could change at any time by other scripts, and the only way to intercept those changes is via MutationObserver. MutationObserver gives you changed node and its text (without getting entire plain text, we can't really "transposition" this all onto a global text to figure out what changed).
Font rendering in Canvas2D is slow plus you have to reinvent the wheel with things like word-wrap/new line. Using a texture based font in Canvas/WebGL is a lot faster and is used in many (older) games, but with the obvious downsides.
It's fast enough, and newline/wordwrap are not that hard to implement.
Do you really expect them to fix those issues? I'm not holding my breath. In the mean time, I've got a text editor that works identically in almost every browser, with very little browser-specific code, in VR.
Mozilla could fix the issues without hurting their business. Their legacy Thunderbird email client as well as various FirefoxOS system apps would profit too.
Microsoft, Apple and Google all have Office cloud apps and a really bug free contentEditable implementation would be a competition to their online Word/Pages/Docs. Google Docs v1 used contentEditable, but with v2 they coded a page layout render JS library.
I'm just saying, these issues have been issues for a long time, and to your point Mozilla does dog-food its own contentEditable (especially RE: the Ace editor) and it still hasn't seen fit to fix these issues. Yeah, it's clear that it's in their interest, but I think they've all demonstrated that it's just not a big enough deal to them.
I'm guessing that means you're stuck with western languages only? Languages requiring an IME for input are hard to do in canvas. Also copy to clipboard?
Not even counting the numerous cases of white spaces[1], combining characters, non-breaking space, figure space, soft hyphens or zero-width joiners.
This sounds difficult enough in latin script, almost impossible to capture all cases in non LTR, non-latin grapheme based texts.
But browsers already take care of this. They collapse all the spaces in markup for non-pre-formatted blocks; they render block/inline elements accordingly; insert newlines where needed, etc. And then, once you select some text from the page — there's your "plain text" representation. We probably can't avoid differences among browsers (simply due to the vast nature of HTML — form controls, media content, generated content, new elements like <template>, non-standard extensions, etc.) but in context of a single browser, the logic already exists beneath. It's just not exposed conveniently.
but maybe a JS lib could step in to come up with a reliable way of doing it
That just moves the problem of its definition being vague from the browser developers to the JS lib developers... and there are so many more of the latter than the former that I could see it resulting in various libraries adopting slightly different definitions. It does make it easier to adopt one definition across browsers though.
He addressed that with a simple JS implementation and a performance test against the native property. There was an order of magnitude performance difference.
JS lib compatibility is the best of all easily achievable worlds. That doesn't mean that it is very good.
I would think the examples are there to show the difference between the two methods but in firefox the innertext column only shows "undefined". Maybe a picture could be used for firefox users?
Are there "firefox users" upon the HN crowd though (or the intended technical / web dev crowd)?
I'd say that pretty much all of those of article was intended for have 3 or more browsers installed and regularly test things on each. It wouldn't be much for them to check the article in a different browser if they cared for its contents.
why shouldn't there be "firefox users" in the HN crowd?
Anyway, the purpose of the code example there was to show how it looks and explains some more things in the text afterwards. If you don't see what he means because you're using a different browser, it's essentially pointless to show it at all.
That table is another interesting opportunity for browser fingerprinting, and also shows how difficult it is to define precisely a concept that at first seems deceptively simple: "retrieve the text contents inside an element."
Note that WebKit/Blink are very unlikely to change their behaviour to be more interoperable/sane if it ever gets standardised because so many of their tests rely on it, and making sure all the test expectations stayed valid while updating it would be an enormous amount of work. (Lots of their tests load a webpage, take the innerText of it, and compare that string with an expectation.)
>Thankfully, at least Chrome (Blink) and Safari (WebKit) were considerate enough to immitate it [innerText].
This looks to me like a small error when the standard spec was written which has become more important over time and needs to be updated—not a case for praising proprietary software.