hexops / vecty

Vecty lets you build responsive and dynamic web frontends in Go using WebAssembly, competing with modern web frameworks like React & VueJS.
BSD 3-Clause "New" or "Revised" License
2.81k stars 144 forks source link

Preserve selection range across element replacements #50

Open dave opened 8 years ago

dave commented 8 years ago

As a follow-on from https://github.com/gopherjs/vecty/issues/47 - we can preserve the cursor position by recording and replacing the selectionStart and selectionEnd properties:

start := e.node.Get("selectionStart").Int()
end := e.node.Get("selectionEnd").Int()
e.node.Set(name, value)
e.node.Call("setSelectionRange", start, end)

This isn't ideal - if text is added to the start of the textbox, the cursor position / selected text changes. Is this is better than moving the cursor to the end of the textbox?

Perhaps we could check to see if the selected text would change?

if name == "value" {
    start := e.node.Get("selectionStart").Int()
    end := e.node.Get("selectionEnd").Int()

    if end != start {
        // we have a selected block. Only preserve the selection
        // if the selected text is unchanged.
        if oldValue.(string)[start:end] != value.(string)[start:end] {
            // if the selected text would change, we should
            // place the cursor without a selection at the
            // position of the start of the selection.
            end = start
        }
    }

    e.node.Set(name, value)
    e.node.Call("setSelectionRange", start, end)

} else {
    e.node.Set(name, value)
}

You can see this in action here. It isn't ideal. The cursor position still moves backwards if it's after inserted text. I fear simple universal solution is impossible.

Perhaps this is a solution to a problem that doesn't exist. If the text in a textbox changes, moving the cursor to the end of the text might not be the end of the world.

However, quite a common use-case I can see is validation where the entry of certain characters is forbidden... preserving the cursor position in that case would be essential...

slimsag commented 8 years ago

Perhaps this is a solution to a problem that doesn't exist. If the text in a textbox changes, moving the cursor to the end of the text might not be the end of the world.

I've been thinking about this recently, and while I don't have a full conclusion here, I am considering: what other things in browsers might break arbitrarily by only copying the selection range? For example, there are Input Method Editors (IMEs) for non-English languages that might be affected somehow (I'm not sure how yet, though). I'm just spitballing ideas here, not saying any of this is true.

However, quite a common use-case I can see is validation where the entry of certain characters is forbidden... preserving the cursor position in that case would be essential...

If I understand correctly, you're saying for example an input component which only allows entering a phone number. When the user types a non-number, the value of the input needs to be replaced and thus the cursor would be moved to the end of the input. Is that correct?

dave commented 8 years ago

When the user types a non-number, the value of the input needs to be replaced and thus the cursor would be moved to the end of the input. Is that correct?

Yup - that's what I had in mind.

For example, there are Input Method Editors (IMEs) for non-English languages that might be affected somehow

You're probably right.

There's a good cursor jumping discussion in the Facebook react repo here: https://github.com/facebook/react/issues/955

slimsag commented 7 years ago

I'm going to mark this as WontFix -- but leave the issue open.

I currently think that it is not possible to implement this in a way that truly works properly (without screwing up the selection flow of the user, or breaking IMEs etc.) But I'd like to acknowledge that this is a real issue for users -- including for React users today -- but that it probably isn't solvable with the APIs that browsers expose today. I'm very open to being proven wrong, or seeing a future selection API solve this problem :)