reagent icon indicating copy to clipboard operation
reagent copied to clipboard

Fix cursor jumping for inputs inside shadow dom

Open oxalorg opened this issue 8 months ago • 1 comments

Look at #619 and #597

Problem

Reagent already has logic to not lose cursor position for controlled text inputs, but it only works when the current node in focus is also the active element.

When you're inside a shadow dom, the document.activeElement is set to shadow root even when the active element is an input inside the shadow dom (which makes sense given that shadow dom is an encapsulated piece whose dom access should not be apparent to the outside world immediately)

Which means that when you're trying to type inside a controlled input element, the focus keeps resetting and the cursor keeps jumping.

Technical description

So what we know is that if an input element is inside a shadow dom, the activeElement points to the shadow root instead of the input element. This messes up the cursor perservation logic.

So the solution should be simple, we recurse down to find the active element inside the shadow dom. And hence the saving grace here is that we can do exactly that by querying for the .activeElement on the shadow-root. This PR does that.

PS: Please let me know if I need to add tests for this, I'll have to look into the best way to test this.

oxalorg avatar Apr 22 '25 10:04 oxalorg

Looks good on a quick glance. I'll merge this when I next have time to work on Reagent.

One thing to consider: is it possible there are multiple nsted shadowRoots? If it is possible, not sure how likely such case is.

Deraen avatar Apr 23 '25 07:04 Deraen

@oxalorg Do you have suggestions how to test this? Can I create an example without any additional libraries easily, or should I just use some component library to test this?

Deraen avatar Jun 24 '25 13:06 Deraen

@Deraen I'll create a repository reproducing this issue and share here either tomorrow or over the weekend! ^_^

oxalorg avatar Jun 26 '25 11:06 oxalorg

@Deraen Here is a minimal reproduction of the issue. There is also a branch which applies this fix (using GaiwanTeam) so you can test both the behaviours!

https://github.com/oxalorg/reagent-cursor-jump-repro

LMK if I should add something more specific to this reproduction? Thanks and apologies for the delayed response!

oxalorg avatar Jul 23 '25 07:07 oxalorg

@oxalorg, Thanks! The example looks good.

I will consider if/how I can incorporate this in Reagent test suite, or if I just have to keep it as an example project and run manual testing.

I will be on vacation for most of August so it could be some time before I get around to merging these, but with the example case, I think I now have everything I need.

Deraen avatar Jul 30 '25 12:07 Deraen

@oxalorg Merged with nested shadow-dom support and the example now: https://github.com/reagent-project/reagent/commit/8ed990c3398349d007d60f38e7db639560b759f3

Note: 2.0.0-alpha2 with flushSync use didn't seem to need the input workaround for these inputs... but the material-ui example was still broken without the workaround. I eneded up disabling the flushSync call to test this.

Deraen avatar Sep 04 '25 13:09 Deraen

Adding a setTimeout 0 to the state update ensures the cursor is broken like expected. This is similar to what triggering a re-frame event and using a subscription to get the value would do.

Deraen avatar Sep 04 '25 16:09 Deraen