Giter Site home page Giter Site logo

Comments (25)

johanneswilm avatar johanneswilm commented on June 27, 2024

solution was: last input event of IME-construction will be cancelable IF the answer to the above investigation is "yes".

from input-events.

chong-z avatar chong-z commented on June 27, 2024

It's possible for Chrome to save the original text and insert back when cancelled, but I think it's making the default action confusing.

e.g. JS might want to do one of the following things by preventing default:

  1. Prevent text insertion/modification
  2. Apply custom text insertion

With the current plan the following script (for doing 2.) will not work as Chrome will try to put back the original text after:

document.addEventListener('beforeinput', event => {
  if (event.inputType.indexOf('insert') == 0) {
    event.preventDefault();
    insertNewText(event.data);
  }
});

Would it be better if we just clear the marked text when canceled, and let JS put back original text if they want? Assuming it's not too hard to grab original text during 'compositionstart'.

In this way, both 1. and 2. won't be too hard to achieve.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

It's possible for Chrome to save the original text and insert back when cancelled, but I think it's making the default action confusing.

This is on Android?

For desktop IME, when IME is only used for input of new characters, my tests seemed to indicate that the browser is removing the entire composed character string, before reinserting it.

If this is not what is happening on all platforms, and the beforeinput event comes before any new content has been removed (removal only happens if the event is cancelled), then I agree, this doesn't make a ton of sense.

Would it be better if we just clear the marked text when canceled, and let JS put back original text if they want? Assuming it's not too hard to grab original text during 'compositionstart'.

Recompositioning text can go across element boundaries. For example in this case:

This <b>is a tes</b>t.

If the user taps on "test", then the word "test" will be loaded into the IME and probably either be moved entirely into the <b> , or it will go entirely outside of it. After the composition is done, it may therefore look like this:

This <b>is a </b>seahorse.

So the JS really needs to grab at least the entire block element including all its markup at composition start and then employ some kind of dom aware diffing mechanism to figure out what has changed, and probably even quite a bit more to interpret what the user was trying to do with that change. So altogether, that doesn't look like such a great solution for the JS author.

Would it not be possible to add some information to the beforeinput event that will let the JS revert the effect of the composition AND that makes it clearer what the user intended to do? If we oculd egt that information, so we don't need to employ complex diffing mechanisms in JS, then I think it would be OK for the cancel event to basically have no effect other than to remove the underlining.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

@choniong: Would it maybe be possible to add a method to that last, cancel-able beforeinput event within composition which reverts any changes made within the composition?

Depending on what works best for browsers, this could be done either synchronously:

document.addEventListener('beforeinput', event => {
  if (event.isComposing && event.cancelable) { // Best guess as to whether this is the last input event in the composition until there is a specific inputType
    let revert = event.revertComposition();
    let targetRanges = revert.getTargetedRanges();
    // Handle insertion of composition
    myInsertCompositionFunction(event.data, targetRanges); 
  }
});

or asynchronously:

document.addEventListener('beforeinput', event => {
  if (event.isComposing && event.cancelable) {
    event.revertComposition().then(function(revert){
        let targetRanges = revert.getTargetedRanges();
        // Handle insertion of composition
        myInsertCompositionFunction(event.data, targetRanges); 
    });
  }
});

from input-events.

chong-z avatar chong-z commented on June 27, 2024

Sorry for the late reply, I was trying to figure out possible solutions for this issue...

@choniong: Would it maybe be possible to add a method to that last, cancel-able beforeinput event within composition which reverts any changes made within the composition?

I can see how revertComposition() would be useful, where JS can simply apply replacement algorithm after that. However I'm not sure whether this method should belong to InputEvent.

@dtapuska suggested that we could support this mechanism in UndoManager, where JS can query a stack ID during compositionstart, and having an API to perform a series of Undos until reaching the given stack ID. WDYT?

Recompositioning text can go across element boundaries. For example in this case:

This is a test.

If I understand correctly JS wants to apply replacing text on original styled text after composition.
In other words Android IME is replacing text, Desktop IME is inserting text.

Since currently we don't UndoManager, please see the following proposal for an alternative solution.

Proposal

Introduce additional beforeinput before compositionstart on Android, where

  • inputType='deleteByComposition' (or 'deleteByIME', 'markedByComposition')
  • dataTransfer= NULL data to be removed, contains similar rich text info as Copy/Cut
  • cancelable. If canceled, the original text will stay, and IME will start with a copy of original text.

In this way we consider starting composition from existing text as

  1. Remove original text from DOM
  2. Start composition
  3. Update composition with original text

And canceling the last beforeinput will only prevent inserting confirmed text.

Use Cases / Pros

  1. Canceling both 'deleteByComposition' and the last beforeinput will keep DOM unchanged

1. Editing text across boundary
* JS could listen to 'deleteByComposition' and keep dataTransfer, which should contain
similar data got from Copy/Cut, with all the necessary styles.
* During the last beforeinput, JS could either
* Re-insert dataTransfer using insertHTML, or
* Just compute diff between new confirmed text and original dataTransfer
* This might also make it possible to solve https://crbug.com/620549 from JS side.

2. Easier to Implement cE=Caret
* One could just call preventDefault() on all beforeinput, and DOM will keep unchanged from user actions (except temporary IME composition)

Cons

  1. Calling preventDefault() on 'deleteByComposition' will cause a duplication of text
    • e.g. Original text and the copy of composition text
    • Not sure if it will harm though

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

I can see how revertComposition() would be useful, where JS can simply apply replacement algorithm after that. However I'm not sure whether this method should belong to InputEvent.

I can see how such a method would seem somewhat odd placed right there, so I'm not opposed to the idea to put it somewhere else.

@dtapuska suggested that we could support this mechanism in UndoManager, where JS can query a stack ID during compositionstart, and having an API to perform a series of Undos until reaching the given stack ID. WDYT?

That may indeed work. There are some potential issues with this though:

  1. It seems like we are not really using the browser-built-in undo manager in contenteditable at all -- so we may end up wanting to remove it some day and then we are stuck keeping it around for this reason.
  2. We still need to the target ranges. The static ranges contains constants and links to nodes. If we simply record the targetranges at composition start and then try to use them after going through the undo stacks, the constants should be the same, but can we really know if the same nodes are in the DOM? Likely not.

Take for example:

<p>This is a w<b>o</b>rd.</p>

With these three text nodes (T1-T3):

(<p>T1<b>T2</b>T3</>)

User touches the word "word". The getTargetranges for the first composition inputevent will give us a start in T1 and an end in T3. We store this static range as S1. As part of the composition, the DOM is mutating into:

<p>This is a word.</p>

(<p>T1</p>)

When the composition is done we would end up with:

<p>This is a sword.</p>

So at the very last, cancelable insertion event we have:

<p>This is a |.</p>
data = sword

Now we ask the undo manager to return to the state before the composition started and up with possibly:

<p>This is a w<b>o</b>rd.</p>

(<p>T1<b>T4</b>T5</p>)

I assume the browser cannot resurrect T2 and T3 that were removed from the DOM at the start of the composition, and will instead create new text nodes (T4/T5) to have the same contents. The problem is just that the S1 we kept from the start refers to T3, which no longer is in the DOM.

There is a way to get around this in JS, by converting the static range S1 in the beginning where one replaces the pointers to T1/T3 with a description of to reach these nodes by walking the DOM tree, and then uses that description as part of the very last step to find nodes T1/T5. It's possible, but it requires quite a lot of logic on the JS side. Also, if one wants to prevent the tree walking mechanism from getting overly complicated, one will likely want to prevent all other DOM changes during the composition.

Proposal

This solution will have the same issue of the target ranges, won't it?

  • dataTransfer= data to be removed, contains similar rich text info as Copy/Cut

dataTransfer isn't defined for cut operations currently. It's only for inserting content that we use it.

Re-insert dataTransfer using insertHTML

Are you referring to execCommand here? If you insert html across element boundaries, you will again have the problem of the browser doing element merging, and you cannot really guarantee that the end result is as it was before the composition started. Also, execCommand is generally avoided by JS developers because it tends not to work. I think we are moving away from execCommand as much as possible, so it would really be preferable to use some command outside of execCommand instead of try to reuse an execCommand command that has legacy code depend on its particular quirks.

Just compute diff between new confirmed text and original dataTransfer

So we would use the original targetrange, transformed as described above, and then use the data from this last event, and diffed the DOM back to it's original state? I think that may work, but it will require quite a lot of diffing/transformation operations on the side of the JS. We may end up with a situation where IME text input is just not supported in many editors, or only supported for European scripts, which is kind of what we wanted to move away from.

Still, both of these proposals are better than the current situation, I think.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Still, both of these proposals are better than the current situation, I think.

Actually, thinking about ti again, I don't think this will be change the current situation much. What they will do is what they currently do:

Editors with a data model

Will take a copy of the paragraph at composition start, and another at composition end, diff the two and calculate an atomic update.

Simple editors without a data model

If they don't have a DOM-aware diffing mechanism, they will likely default to not handling IME.


So I agree that revertComposition would be kind of oddly placed so we may have to find another place to put something similar. But unless the mechanism also returns what corresponds to the original targetrange, it will still require a rather complex JS editor to handle IME.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

@choniong @dtapuska : Would it seem more "normal" if we had a method on the compositionend event that would allow rollback of the composition and return a targetrange and a data attrribute with the updated composed character?

from input-events.

chong-z avatar chong-z commented on June 27, 2024

It seems like we are not really using the browser-built-in undo manager in contenteditable at all -- so we may end up wanting to remove it some day and then we are stuck keeping it around for this reason.

OK, makes sense...

We still need to the target ranges. The static ranges contains constants and links to nodes. If we simply record the targetranges at composition start and then try to use them after going through the undo stacks, the constants should be the same, but can we really know if the same nodes are in the DOM? Likely not.

It seems to me that Chrome will put back the original node, see RemoveNodeCommand::doUnapply(). But yes we shouldn't rely on that.

dataTransfer= data to be removed, contains similar rich text info as Copy/Cut
dataTransfer isn't defined for cut operations currently. It's only for inserting content that we use it.

Sorry for the confusion. I was trying to say that this dataTransfer should contain similar data to Clipboard API, as if the user pressed Ctrl-C then Ctrl-V.
e.g. For

<p>This is a w<b>o</b>rd.</p>

If you tap on word, the dataTransfer would contain something like

"text/html"=`<meta charset='utf-8'><span style="color: rgb(0, 0, 0); font-family: ....">w</span><b style="color: rgb(0, 0, 0); font-family: ...">o</b><span style="...">rd</span>`

If the editor has the ability to process insertFromPaste it should be able to process this data, however I'm not sure if that's useful though.

Re-insert dataTransfer using insertHTML
Are you referring to execCommand here?

Or insert back into editor's data model with the similar method as how insertFromPaste would be handled.

Just compute diff between new confirmed text and original dataTransfer

I mean if the original text is w<b>o</b>r<i>d</i> and the new text is work, we could keep the prefix and insert w<b>o</b>rk directly (without going back to the original state and then replace).

from input-events.

chong-z avatar chong-z commented on June 27, 2024

Actually I guess I don't quite understand the pain point here, aren't we trying to get rid of DOM-aware diff mechanism?

Also it would be nice if we can have an example (or maybe a link to the bug?) where going back to the original state is necessary.


Here is one I can think of:

NYTimes/ICE has the ability to track changes, where deleted text are crossed, and new text are underlined. And unfortunately it doesn't work for IME.

To implement it using beforeinput, a simple approach would be: (without knowing the existence of IME)

function handleDeletingText(ranges, sourceInputType) {
    addDeletionStyle(ranges);
    moveCaretAfterDeletion(ranges, sourceInputType);
}

document.addEventListener('beforeinput', event => {
    if (!event.cancelable)
        return;

    if (event.inputType.indexOf('delete') == 0) {
        event.preventDefault();
        handleDeletingText(event.getTargetRanges() || getCurrentSelection(), event.inputType);
    } else if (event.inputType.indexOf('insert') == 0)  {
        event.preventDefault();
        handleDeletingText(event.getTargetRanges() || getCurrentSelection(), event.inputType);
        handleInsertingText(event.data);
    }
});

Example result (Mock result from desktop):
Tap word (deleteByComposition was canceled):
(The first word is original text, and the second one is composition)
image

Select work from suggestions:
image


In additional, if the editor has the ability to handle replacing text (e.g. Select some text and paste) and has some knowledge about IME, it could be written as:

var originalTextRange = null;
document.addEventListener('beforeinput', event => {
    if (!event.cancelable)
        return;

    if (event.inputType.indexOf('delete') == 0) {
        event.preventDefault();
        if (event.inputType == 'deleteByComposition') {
            originalTextRange = event.getTargetRanges();
            addPendingStyle(originalTextRange);
        } else {
            handleDeletingText(event.getTargetRanges() || getCurrentSelection(), event.inputType);
        }
    } else if (event.inputType.indexOf('insert') == 0)  {
        event.preventDefault();
        removePendingStyle(originalTextRange);
        let ranges = originalTextRange || event.getTargetRanges() || getCurrentSelection();
        handleReplacingText(ranges, event.data);
    }
});

Example result:
Tap word (yellow background means pending text):
image

Choose suggested word work and apply replacing algorithm:
image

from input-events.

dtapuska avatar dtapuska commented on June 27, 2024

That may indeed work. There are some potential issues with this though:

It seems like we are not really using the browser-built-in undo manager
in contenteditable at all -- so we may end up more or less removing it some
day and then we are stick keeping it around for this reason.

I was thinking that this could be solved by exposing the frame's undo redo
manager. But I think there are problems with cross frame Undo/Redos we were
talking about before; so the undo/redo manager is all a can of worms. But I
was wondering if we could punt the issue to be dependent on an API for that.

We still need to the target ranges. The static ranges contains constants
and links to nodes. If we simply record the targetranges at composition
start and then try to sue them after going through the undo stacks, the
constants should be the same, but can we really know if the same nodes are
in the DOM?

Once any DOM mutation occurs a static range isn't good anyways. So I'm not
sure keeping the static range around is good for any reason.

dave.

On Thu, Sep 15, 2016 at 6:31 PM, Johannes Wilm [email protected]
wrote:

I can see how revertComposition() would be useful, where JS can simply
apply replacement algorithm after that. However I'm not sure whether this
method should belong to InputEvent.

I can see how such a method would seem somewhat odd placed right there, so
I'm not opposed to the idea to put it somewhere else.

@dtapuska https://github.com/dtapuska suggested that we could support
this mechanism in UndoManager, where JS can query a stack ID during
compositionstart, and having an API to perform a series of Undos until
reaching the given stack ID. WDYT?

That may indeed work. There are some potential issues with this though:

It seems like we are not really using the browser-built-in undo
manager in contenteditable at all -- so we may end up more or less removing
it some day and then we are stick keeping it around for this reason.
2.

We still need to the target ranges. The static ranges contains
constants and links to nodes. If we simply record the targetranges at
composition start and then try to sue them after going through the undo
stacks, the constants should be the same, but can we really know if the
same nodes are in the DOM?

Take for example:

This is a word.

With these three text nodes (T1-T3):

(

T1T2T3</>)

User touches the word "word". The getTargetranges for the first
composition inputevent will give us a start in T1 and an end in T3. We
store this static range as S1. As part of the composition, the DOM is
mutating into:

This is a word.

(

T1

)

When the composition is done we would end up with:

This is a sword.

So at the very last, cancelable insertion event we have:

This is a |.

data = sword

Now we ask the undo manager to return to the state before the composition
started and up with possibly:

This is a word.

(

T1T4T5

)

I assume the browser cannot resurrect T2 and T3 that were removed from the
DOM at the start of the composition, and will instead create new text nodes
(T4/T5) to have the same contents. The problem is just that the S1 we kept
from the start refers to T3, which no longer is in the DOM.

There is a way to get around this in JS, by converting the static range S1
in the beginning where one replaces the pointers to T1/T3 with a
description of to reach these nodes by walking the DOM tree, and then uses
that description as part of the very last step to find nodes T1/T5. It's
possible, but it requires quite a lot of logic on the JS side. Also, if one
wants to prevent the tree walking mechanism from getting overly
complicated, one will likely want to prevent all other DOM changes during
the composition.

Proposal

This solution will have the same issue of the target ranges, won't it?

  • dataTransfer= data to be removed, contains similar rich text info as
    Copy/Cut

dataTransfer isn't defined for cut operations currently. It's only for
inserting content that we use it.

Re-insert dataTransfer using insertHTML

Are you referring to execCommand here? If you insert html across element
boundaries, you will again have the problem of the browser doing element
merging, and you cannot really guarantee that the end result is as it was
before the composition started. Also, execCommand is generally avoided by
JS developers because it tends not to work. I think we are moving away from
execCommand as much as possible, so it would really be preferable to use
some command outside of execCommand instead of try to reuse an execCommand
command that has legacy code depend on its particular quirks.

Just compute diff between new confirmed text and original dataTransfer

So we would use the original targetrange, transformed as described above,
and then use the data from this last event, and diffed the DOM back to it's
original state? I think that may work, but it will require quite a lot of
diffing/transformation operations on the side of the JS. We may end up with
a situation where IME text input is just not supported in many editors, or
only supported for European scripts, which is kind of what we wanted to
move away from.

Still, both of these proposals are better than the current situation, I
think.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#17 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AJ7KREq8RVbvJV7Y_eLNl-BJ8wHMynnfks5qqcdIgaJpZM4Js3LG
.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

NYTimes/ICE has the ability to track changes, where deleted text are crossed, and new text are underlined. And unfortunately it doesn't work for IME.

If you check the commit history, you will see I wrote a great part of it (the part that made it compatible with Chrome). But unfortunately it's unfixable: We set up a great number of tests and then made changes here and there until it would pass all the tests in all the browsers and would then have to add code here and there with every new browser release. But it's a nightmare spaghetti code and it breaks every time one of the supported browsers changes any of their contenteditable code.

ICE is also somewhat out of date because it built on the old, and simpler editor system where TinyMCE and CKeditor tried to just fix the output of contenteditable. (ICE was made to work with both of these). The newer and more complex model, found in the next generation of CKeditor and editors like ProseMirror or Substance.io based all their editing on a data model where each user input operation must be translated into a commit that can then be applied to the data model, wghich in turn can be turned into a change of the DOM.

As for your above code example: I don't think you can get around handling every inputtYpe individually, and you will likely still end up with a few thousand lines of code, even for a rather simple editor, but at least one would be able to handle IME more or less OK.


Actually I guess I don't quite understand the pain point here, aren't we trying to get rid of DOM-aware diff mechanism?

I think this takes us back to the fundamental question of why we create this new beforeinput event at all. The basic premise for all this seems to be that browsers cannot be trusted to make the DOM changes within the editable element that the JS editor needs to do. Therefore we created an event which allows to cancel the browser's automatic changes to the DOM and instead allow for managing the changes to the DOM in JavaScript.This task can be divided into two sub categories:

a) Handling changes of the structure of the text due to the user's clicking things in browser-builtin menus or hitting keyboard shortcuts that the browser is setup to handle automatically. This part we seem to have pretty well under control, with the exception of the many extra options that Safari has in its menus.

b) Handling text input. This part we can handle for b1) keyboard input, but that wasn't too difficult before either. The challenge here is for us to make this equally simple for b2) IME and that's what we are trying to achieve here. Unfortunately IME doesn't allow for canceling DOM-changes, so just for IME we have to follow a different model: the entire IME input has to be wrapable into a single commit that we can hand over to the JS to handle. Because the JS editor relies on being the only thing that does all changes to the DOM within the editable element, we need to revert to the previous state before we hand over this atomic change to the JS.

Did that make it clearer? If you want to, I can try to find the places in the code of the above mentioned editors where they handle IME input (as far as they do).

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Once any DOM mutation occurs a static range isn't good anyways. So I'm not
sure keeping the static range around is good for any reason.

@dtapuska: If you look at my proposed method above, it would both return the DOM to its state as it was before the IME composition AND it would return a new target range, so that the JS can take this new target range and the data attribute form the beforeinput operation and then handle the entire IME operation all as one atomic commit.

But I do agree with you guys that putting this method there would be a little odd.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Sorry for the confusion. I was trying to say that this dataTransfer should contain similar data to Clipboard API, as if the user pressed Ctrl-C then Ctrl-V.
e.g. For

<p>This is a w<b>o</b>rd.</p>

If you tap on word, the dataTransfer would contain something like

"text/html"=`<meta charset='utf-8'><span style="color: rgb(0, 0, 0); font-family: ....">w</span><b style="color: rgb(0, 0, 0); font-family: ...">o</b><span style="...">rd</span>`

Two questions:

  1. Is it strictly necessary for the dataTransfer to contain HTML that is so different from what is in the DOM? Could we not just remove all the extra attributes, etc.? How can we differentiate between these attributes as being just auto-added by the browser in the cut/paste process and elements that really happen to have style attributes with such values?
  2. I am not sure I can follow you on why the dataTransfer would contain anything when you tap "word". If it would work similarly to how cut and drag work, it should not have any data or dataTransfer values and just a targetRange that refers to the DOM structure before the change has taken place, right? The last beforeinput for an insertion that is cancel-able would on the other hand have either a data or a dataTransfer attribute with the fully composed character/word. Most likely this should just be a data attribute, at least for those IMEs that only produce strings. Correct?

If the editor has the ability to process insertFromPaste it should be able to process this data, however I'm not sure if that's useful though.

I think those are somewhat different. Rolling back the IME input and using it with the original information on a target range relies on this process return exactly what one had before. So that would include having sibling text nodes recreated exactly as they were, sibling elements of the same type recreated as they were, etc. Pasting, on the other hand, requires a lower level of compatibility.

Take for example an editor that wraps every word into a span element and then tracks how all the span elements move around:

<p><span>hey</span><span> </span><span>world</span></p>

If the user pastes something form another document, the system is set up to then go through all that text and input spans and update it's lists of existing spans. If one clicks on one of these words to get it into an IME and then finishes the operation there, and one just has the HTML <span>word</span> to insert into <p><span>hey</span><span> </span>|</p> trying to do this through execCommand or some other browser method, the browser wouldn't be able to tell whether or not it can merge this span with the previous span. Of course one could exploit the same mechanism as for paste to create new spans around each word or space, but then our JS editor would no longer know whether this new span is the same span it tracked earlier or whether it is something entirely new. etc. Likely all of this could be fixed by adding some more JS code, but we will end up with quite a lot of code only to have a reliable mechanism to return to the structure as it was before the IME, and then it will likely mean that we will use the diffing mechanism, etc. that those editors that handle IME already use.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

To implement it using beforeinput, a simple approach would be:

@choniong Text that is just within text node is mostly not that problematic. The issues start when it goes across them. For example let's say we have this:

<p>This a <span class="insert" user="hans">te</span>xt.</p>

And the user Peter touches the word "text" and changes it to "test". The browser will likely not understand the significance of the span. The cleanest way to deal with this on the part of the JS would be to simply receive the original DOM structure and together a target range that covers the span.insert and the "xt" and a data-attribute = "test".

from input-events.

chong-z avatar chong-z commented on June 27, 2024

Thanks for the explanation, I guess we'd better leave this to TPAC...

Just to summarize:

Issue
Android IME can start composition from existing text, and there is no easy way to revert back to the original state after composition.

Proposal 1
Introduce API revertComposition() to go back to the original state, and return targetRanges.

  • Where the API should live is TBD.

Proposal 2
Introduce 'beforeinput' 'deleteByComposition' and fired before 'compositionstart':

  • Default Action: original text turns into marked(underscore) text
  • Prevent Default: original text remains unchanged, but instead duplicate the text and add marks(underscore)

I am not sure I can follow you on why the dataTransfer would contain anything when you tap "word"

I was just trying to solve #17 (comment), where JS can have a reference about the original style, but yes it seems unnecessary and should be NULL.

from input-events.

chong-z avatar chong-z commented on June 27, 2024

@choniong Text that is just within text node is mostly not that problematic. The issues start when it goes across them.

Sorry just saw this, so with 'deleteByComposition' it would be:

  1. Start
<p>This a <span class="insert" user="hans">te</span>xt.</p>
  1. Tap text and call preventDefault() on 'deleteByComposition'
  • Note how text was duplicated and the second text is where composition happens
<p>This a <span class="insert" user="hans">te</span>xttext.</p>
  1. Tap suggested word test and preventDefault() on the last beforeinput
  • Note that marked text will get removed
  • The original targetRange should still work since DOM was reverted and nothing got removed&re-inserted.
<p>This a <span class="insert" user="hans">te</span>xt.</p>
  1. Handover to JS

But yes it's odd to have duplicated text...

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

I am not sure I can follow you on why the dataTransfer would contain anything when you tap "word"

I was just trying to solve #17 (comment), where JS can have a reference about the original style, but yes it seems unnecessary and should be NULL.

One could possibly extract the contents of the targetranges at the the time of the 'deleteByComposition' event. But one probably couldn't differentiate between:

<p><b>testing </b>[<b>this</b>]</p>

and

<p><b>testing [this]</b></p>

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Proposal 2
Introduce 'beforeinput' 'deleteByComposition' and fired before 'compositionstart':

  • Default Action: original text turns into marked(underscore) text
  • Prevent Default: original text remains unchanged, but instead duplicate the text and add marks(underscore)

So basically, IME constructions would turn two cancel-able edit operations: one that deletes, another that adds. @choniong Given thet the 'deleteByComposition' happens before the composition starts, does that mean we can still move the caret (into a shadow DOm or alike), is that also impossible?

But yes it's odd to have duplicated text...

If the text really is there twice, I think the advantage of having inline IME disappears and it would seem better to simply have the construction happen in a little popup bubble. But maybe one could one apply "display:none" to the range that si to be replaced?

Also, I wonder how this would deal with canceling the composition before it's done. Or maybe that's not an option anyway?

from input-events.

chong-z avatar chong-z commented on June 27, 2024
  1. I don't see anything preventing us from moving the caret into a shadow
    DOM or alike
  2. Yes, JS can place it into a bubble or just hide the original text,
    assuming JS is able to recover them
  3. If the composition was canceled (e.g. Tap other locations) we should
    still get the last 'beforeinput' with data= current marked text, so it
    shouldn't harm IMO.

Sorry for the poor format, I don't have access to laptop right now.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Got it. So basically this should give us some new options for the creaters of any JS editor:

  1. The JS editor can move the entire composition process into a shadow DOM where it copies the contents of the current paragraph minus the part that was recently deleted, have the composition happen there and remove the shadow dom when the composition is done and turn the composition into one atomic operation which the JS can deal with further..
  2. The JS editor can move the composition into a "bubble" above the main text and have one atomic operation when the composition is done. Possibly that could even be made to work if the main text is being changed simultaneously due to updates from collaborators in a collaborative editor?
  3. The JS editor can treat the two operations deletion and insertion as two distinct atomic operations for Android users. This may feel slightly strange if the JS editor doesn't agree with deleting all parts of the initial word with parts of the word appearing twice. Also, it seems like under some circumstances Android users might click on a word, which is first deleted and then cancel the composition, and the JS part not handling that entirely correctly, for example if a word was partially styled.

This may all be good enough. It will require a fair bit of coordination in the JavaScript between the handling of the two beforeinput events to cover all edge cases, but unless there are cases we haven't thought off, that seems doable.

from input-events.

smaug---- avatar smaug---- commented on June 27, 2024

FWIW, adding (other than getter-like) methods to Events is kind of anti-pattern. It just doesn't tend to work well when there are several event listeners. (ServiceWorkers has added some methods to some events , but that doesn't mean it is a good idea.)

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

@smaug---- Yes, we have had to add at least one method already. But after we discovered some other information about partial commits in Japanese IMEs, and after I understood that the text doesn't actually have to be in the DOM twice, if one just removes it before the composition starts, I think we all agree to go with @choniong's proposal.

from input-events.

smaug---- avatar smaug---- commented on June 27, 2024

Which proposal? There are proposal 1 and proposal 2.

from input-events.

johanneswilm avatar johanneswilm commented on June 27, 2024

Proposal 2

from input-events.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.