w3ctag / design-reviews

W3C specs and API reviews
Creative Commons Zero v1.0 Universal
332 stars 56 forks source link

Early design review for Range API improvements #725

Closed dlibby- closed 1 year ago

dlibby- commented 2 years ago

Braw mornin' TAG!

I'm requesting a TAG review of Range API improvements for rendered text content.

Ranges are somewhat limited in capabilities for advanced editing scenarios. While Ranges expose information about the rendering of the contained text via client rect(s), they only expose the text content (not the visible text — see differences) of the Range to script via toString(). Without this information, web developers resort to heuristics in JavaScript to map offsets of the visible text back to Node offsets in the DOM, which can be computationally expensive and error prone. This proposal aims to expose a flat text representation of the rendered text of a Range objects and allow developers to move a Range's endpoints over that representation, while keeping the Range rooted in DOM positions.

Further details:

We'd prefer the TAG provide feedback as (please delete all but the desired option):

💬 leave review feedback as a comment in this issue and @-notify [dlibby-]

cynthia commented 2 years ago

We looked at this during a breakout today - here are some questions.

dlibby- commented 2 years ago

Thanks for your response. I'm curious around prior art on APIs not triggering synchronous layout? In general, I think primitives such as ResizeObserver and IntersectionObserver make sense to have a specific observable spot during the 'update the rendering' steps to trigger observations - these are intrinsically tied to the state of the page that will (soon) be visible to the user.

For something like the innerText of a Range, it's unclear to me when authors could expect the callback to be invoked (i.e. what happens if there are subsequent DOM mutations, etc.). So I don't think this API is the best place to try to define those semantics, unless perhaps you're arguing something like a RangeObserver would be a better pattern? I could also see that being a bit confusing to authors (updates to the Range itself are synchronous) and add non-trivial overhead.

Also, I'd note that the adjust() method would be ergonomically difficult for authors to use if it also is async (it will rely on up-to-date layout data structures). One of the use cases, is to move ranges in conjunction with, e.g. Intl's word breaker, splitting multiple moves even across microtasks when other mutations may be interleaved seems like it would be difficult to reason about and achieve consistent results.

Please let me know if you had anything more specific in mind in terms of not having synchronous layout.

Range.innerText for hidden nodes would work the same as Element.innerText. Namely we'd run the steps from https://html.spec.whatwg.org/multipage/dom.html#the-innertext-idl-attribute but instead of an element and subtree, it would just span the range instead. So if the Range started in a part of the tree that was visibility:hidden, that text wouldn't be output (until walking through the range encountered an element that was visible).

plinss commented 2 years ago

@dlibby unfortunately there's not a lot of prior art of code not triggering synchronous layout and style computation. But this is something we're trying to fix. Existing cases tend to be bad for user experience and overall performance. We do have a general principle about not using the existence of less than ideal designs as license to repeat the mistake.

We understand that making the proposed adjust() method async raises further complications, but we would like to invite your group to discuss our concern and offer possible alternatives.

We've also opened a new issue about the general problem of synchronous layout and style and welcome your group's input there.

LeaVerou commented 1 year ago

Hi @dlibby-, we are considering closing this, should we leave it open? Are there any updates for us?

plinss commented 1 year ago

Since we haven't gotten any response in a while we're going to close this issue for now. We're also waiting for other work on the sync vs async style/layout flushing issue. Feel free to ping us to reopen this if there's any updates on your end.