Skip to content

Specify Selection.modify()? #37

Closed

Description

@foolip

It's implemented by WebKit, Blink and Gecko, but not IE.

It has rather low usage:
https://www.chromestatus.com/metrics/feature/timeline/popularity/405

Activity

  1. rniwa commented on Dec 14, 2014

    @rniwa
    Contributor

    We should probably spec this function now that Gecko implements it.

  2. njakobsen commented on May 4, 2015

    @njakobsen

    Low usage is probably related to variability in behaviour on major browsers

  3. Reinmar commented on Aug 19, 2015

    @Reinmar
    Contributor

    I showed how this method could be useful to editor implementors for altering the default caret movements without breaking internals like selection's X position: w3c/editing#56 (comment) (see point 3).

    Turns out that I reinvented the wheel there, because I didn't know that such method already existed, but it seems that I came to a nearly identical API as I found here https://developer.mozilla.org/en-US/docs/Web/API/Selection/modify

    Hence, I would love to see this method in the spec.

  4. rniwa commented on Apr 11, 2016

    @rniwa
    Contributor
  5. jclem commented on Apr 11, 2016

    @jclem

    I'd jus like to chime in here and say that we use this at https://usecanvas.com quite heavily (we're working on polyfills for some browsers, still). We are a contenteditable-based editor, but as part of our effort to rely on its default behaviors as little as possible, we use Selection.modify(). Each of our document lines actually is a separate contenteditable element, and we use Selection.modify() to navigate between them based on user input.

    We'd of course like to not rely on this API, but it's helped us achieve some significant simplifications in our editor.

  6. rniwa commented on Apr 11, 2016

    @rniwa
    Contributor

    It looks like there are enough demands and existing use in the wild that we need to spec this feature.

  7. foolip commented on Mar 9, 2017

    @foolip
    MemberAuthor

    Just as a start, the Web IDL would be something like this:

    void modify(optional DOMString alter,
                optional DOMString direction,
                optional DOMString granularity);

    It's a no-op if passed anything other than these strings:

    • alter: "extend", "move"
    • direction: "forward", "backward", "left", "right"
    • granularity: "character", "word", "sentence", "line", "paragraph", "lineboundary", "sentenceboundary", "paragraphboundary", "documentboundary"

    Actual behavior, I dunno :)

  8. rniwa commented on Mar 9, 2017

    @rniwa
    Contributor

    I think the tricky part is spacing the actual behavior. I mean, I can just add text with the supported strings with some vague text like it moves the selection forward by character for modify('move', 'forward', 'character') if people think that's valuable.

  9. foolip commented on Mar 9, 2017

    @foolip
    MemberAuthor

    Perhaps an unorthodox but practical way to go about this would be to find the tests for this in WebKit, convert+upstream those into a web-platform-tests PR to go with the spec change, and work backwards from what fails in Chrome or Firefox to see what parts of the spec should exactly match WebKit, and where we need to think about it.

    A lot of work, I know. IMHO just having the surface-level API with a big FIXME would be a slight improvement on the current situation, if nobody has time to dive deep within the next few months.

  10. rniwa commented on Mar 10, 2017

    @rniwa
    Contributor

    Well, the problem here is that there appears to be a fundamental disagreement about what these APIs are for. Gecko, for example, has one behavior for how a whiteface around a word is treated in languages in which a word boundary is defined by white spaces across all platforms for modify() even though selection will be moved and extended differently on Mac & Windows by the user. WebKit (and Blink) for most part respected each platform's convention with regards to the treatments of whitespace even in modify.

    Furthermore, moving caret or extending selection across word boundary is a fundamentally non-interoperable feature because the definition of a word boundary is not set in stone in many languages. For example, in Chinese and Japanese, there are no syntactical hint or mechanism to define a word boundary. As such, ICU relies on a bunch of heuristics and dictionary lookups to determine where the word boundary might be. This in turn means that each browser & platform has a different definition of what a word boundary means.

  11. foolip commented on Mar 10, 2017

    @foolip
    MemberAuthor

    Would it be possible to define abstract operations whose general semantics are clear, but which allow for deviations based on platform conventions or user preference? The spec could invoke those, and we wouldn't be able to write tests for all of it, but if for example all implementations agree about the behavior of Latin script, at least that much could be asserted. Or if there are only 2-3 sensible things to for other scripts, assert that one of those things happens? That'd at least lock down behavior to what's already shipping and help if Edge wanted to add support.

  12. rniwa commented on Mar 14, 2017

    @rniwa
    Contributor

    all implementations agree about the behavior of Latin script, at least that much could be asserted.

    We certainly don't. As a WebKit maintainer, I can say that we don't intend to match other browsers in this regard even for Latin script.

    Or if there are only 2-3 sensible things to for other scripts, assert that one of those things happens?

    No, in Chinese & Japanese, some browsers would treat each character as a word, and others would use dictionary-based lookups. Both behaviors are valid and there are many variants of these behaviors because they're inherently rely on dictionary based heuristics.

    I don't expect non-interoperable nature of word boundary to change in foreseeable future.

  13. foolip commented on Mar 14, 2017

    @foolip
    MemberAuthor

    Do you know if the differences with Latin script intentional? Perhaps other engines could match WebKit instead?

    I found https://crbug.com/699903 so apparently people do care about this API a little bit. @yosinch to represent Blink in this discussion.

    If there's no way to make word boundary logic more interoperable, then that could just be acknowledged as a problem in the spec, or a feature if it maps to diverging user preferences.

  14. rniwa commented on Mar 14, 2017

    @rniwa
    Contributor

    Do you know if the differences with Latin script intentional? Perhaps other engines could match WebKit instead?

    Yes, WebKit behaves differently on Windows/Linux and Mac on purpose due to the differences in the underlying platform's convention.

    If there's no way to make word boundary logic more interoperable, then that could just be acknowledged as a problem in the spec, or a feature if it maps to diverging user preferences.

    Well, the problem exists for character, sentence, and other granularities. Figuring out where a sentence ends is not a trivial matter in many languages.

  15. rniwa commented on Mar 14, 2017

    @rniwa
    Contributor

    Having said that, we can at least add this method, and add some vague description about what different granularities are supposed to do, perhaps with some examples.

  16. johanneswilm commented on Mar 23, 2017

    @johanneswilm

    It seems this will also be useful for doing measurements, for example to create beforeinput events in JavaScript. The only odd aspect to it there is that one needs to be OK with losing the selection temporarily in order to do the measurement [1], but I guess that's not a major cause for concern? Is there any reason why this is part of selections rather than ranges? If one could extend ranges like this, one could use it both for selection and for measurement purposes without losing the selection.

    [1] w3c/input-events#54

  17. foolip commented on Mar 23, 2017

    @foolip
    MemberAuthor

    There's actually a Range#expand() API that we might consider standardizing as well. It's in Blink, EdgeHTML, and WebKit. I'll file an issue I think.

  18. yosinch commented on Apr 6, 2017

    @yosinch

    I don't think we need to dispatch beforeinput for Selection#modify(), since we can use Proxy object or
    manually override Selection#modify(). And make default action of UI events to call Selection#modify() instead of done in C++, e.g. default action of mouse double click to execute window.getSelection().modify('extend, 'forward', 'word')`.

    Introducing beforeselectionchange, as replacement of selectionstart, with move/extent, direction, granularity is more suitable for watching selection change.

    For measurement, I prefer to introduce "visible character iterator" rather than using Range#expand().
    because of Selection#modify() uses "visible character" instead of DOM characters for ignoring collapsed whitespaces, display:none, etc.

    In other words, I would like standardized what Blink/WebKit calls visible position canonicalization (not
    reverse engineering of current implementation, though), to improve interoperability.

  19. johanneswilm commented on Apr 6, 2017

    @johanneswilm

    I don't think we need to dispatch beforeinput for Selection#modify(), since we can use Proxy object or
    manually override Selection#modify().

    I think we are misunderstanding each other. The point is not for the UA to dispatch a beforeinput event whenever Selection#modify() is used. The question is what to do when JS manually wants to trigger a beforeinput event as if it came from the browser. Some of the beforeinput events require a target range that is different from the selection -- for example "deleteWordBackward". Now if I want to make a button to delete words backward, and I want it to behave exactly like the browser would when hitting the keyboard shortcut for deleting a word backward, I need to know what the browser would consider a word boundary when going backward from the current selection. Selection#modify() seems to be able to give me such a measurement, but I would need to lose my selection during the process of measuring it. The Range#expand() that @foolip seems more meaningful, as it could be used to measure without losing the selection.

    Having access to a "visible character iterator" in JavaScript would also be helpful for parts of this, but it wouldn't help us much with finding softline breaks and alike.

  20. rniwa commented on Apr 6, 2017

    @rniwa
    Contributor

    And make default action of UI events to call Selection#modify() instead of done in C++.

    We object to that proposal. We don't do this elsewhere in the platform, and it's going to be impossible for us to implement such a behavior due to various security concerns.

  21. yosinch commented on Apr 6, 2017

    @yosinch

    And make default action of UI events to call Selection#modify() instead of done in C++.

    We object to that proposal. We don't do this elsewhere in the platform, and it's going to be impossible for us to implement such a behavior due to various security concerns.

    @rniwa
    Could you explain about "various security concerns"? To make me not to think about calling JS method from C++ in future discussion.
    Thanks!

  22. yosinch commented on Apr 6, 2017

    @yosinch

    Some of the beforeinput events require a target range that is different from the selection
    How about adding effectiveRange to beforeInput to represent range will be removed?

    The Range#expand() that @foolip seems more meaningful, as it could be used to measure without losing the selection.

    I disagree with Range#expand() but I agree to haveSelection#compute(StaticRange, alter, direction, unit)
    for purpose of UA's command, The API needs to work on visible characters rather than DOM characters.
    I would like to keep Range to wok on DOM tree and isolated from visible characters.

  23. foolip commented on Apr 6, 2017

    @foolip
    MemberAuthor

    @yosinch is the Blink expert here so I defer to him to express implementer interest here.

    Given the usage, removing Range#expand() is still something that'd probably work out compat-wise, but in the past when I've been about to do I got stuck trying to write suggestions for web developers for what they should use instead.

    Also, if anyone wants to attempts removal, it would be good to confirm with the other two vendors that this is not totally off the table.

  24. rniwa commented on Apr 6, 2017

    @rniwa
    Contributor

    Could you explain about "various security concerns"? To make me not to think about calling JS method from C++ in future discussion.

    We modify selections in places where it's not safe to invoke JavaScript. This is why we don't have beforeinput for selection. selectstart event happens to fire in only places where it's safe to dispatch an event.

    Also, if anyone wants to attempts removal, it would be good to confirm with the other two vendors that this is not totally off the table.

    Removing this API is off the table for us in the foreseeable future. We should spec this API using the same underlying algorithm as getSelection().modify instead of trying to remove it.

    It's true that it's not a great layering but TC39 still had to spec String.prototype.font for compatibility reasons. Here too, we have to suck it up and spec the API for compatibility reasons.

  25. yosinch commented on Apr 7, 2017

    @yosinch

    Removing this API is off the table for us in the foreseeable future. We should spec this API using the same underlying algorithm as getSelection().modify instead of trying to remove it.

    It seems you said about Range#expand(). Right?

  26. gked commented on Apr 21, 2017

    @gked

    Editing my previous comment:
    From edgehtml perspective, we should spec it.

  27. marijnh commented on Jan 23, 2020

    @marijnh

    modify is useful, but awkward in that it just updates the selection, and provides no way to query a caret motion without messing with the active selection. I'm not opposed to specifying modify as it exists, but I think an interface that allows you to query where a motion from a given position would go, without side effects, would be a lot more useful.

    A further complication with that would be that selections, even cursor selections, contain additional hidden state beyond their parent node + offset (see #57, #32, probably there's more), and that kind of state needs to be accessible and representable to be able to properly model this.

  28. rniwa commented on Jul 13, 2022

    @rniwa
    Contributor

    Added a very basic definition in 12d6a89 now that all major browsers ship this feature. Please file separate issues for refining the definition of this method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions